logo       


Choosing A Webhost:
A web hosting service is a type of Internet hosting service that allows individuals and organizations to provide their own website accessible via the World Wide Web. Web hosts are companies that provide space on a server they own for use by their clients as well as providing Internet connectivity, typically in a data center. Web hosts can also provide data center space and connectivity to the Internet for servers they do not own to be located in their data center, called colocation. more...

[Fwd: [FEISTY]: Ultra45 support]: msg#00038

Subject: [Fwd: [FEISTY]: Ultra45 support]
--- Begin Message ---
Subject: [GIT]: Ultra45 support
I checked in three changes to my ubuntu-feisty tree at:

        kernel.org:/pub/scm/linux/kernel/git/davem/ubuntu-feisty-sparc64.git

The first changeset adds support for the PCI-Express controller
in this box.

The next two add support for the XVR-500 and XVR-2500 graphics
cards, enough for basic kernel console support.

I've attached the three changes as patches if that works better
for you guys.

Is is possible to get a test net-install image with these patches
applied?  For my box I'd need FUSION_SAS, TIGON3, and FB_XVR2500
available.  Essentially the ultra45 looks like a T2000 with a video
card :-)

I know you guys are at the conference, so whenever you get a
chance is fine.

Thanks.


>From c58cccdc977c3f4a68ef3a2eaa30a5c3f6924cab Mon Sep 17 00:00:00 2001
From: David S. Miller <davem@xxxxxxxxxxxxxxxxxxxx>
Date: Mon, 7 May 2007 17:08:38 -0700
Subject: [PATCH] [SPARC64]: SUN4U PCI-E controller support.

Some minor refactoring in the generic code was necessary for
this:

1) This controller requires 8-byte access to the interrupt map
   and clear register.  They are 64-bits on all the other
   SBUS and PCI controllers anyways, so this was easy to cure.

2) The IMAP register has a different layout and some bits that we
   need to preserve, so use a read/modify/write when making
   changes to the IMAP register in generic code.

3) Flushing the entire IOMMU TLB is best done with a single write
   to a register on this PCI controller, add a iommu->iommu_flushinv
   for this.

Still lacks MSI support, that will come later.

Signed-off-by: David S. Miller <davem@xxxxxxxxxxxxx>
---
 arch/sparc64/kernel/Makefile    |    2 +-
 arch/sparc64/kernel/irq.c       |   16 +-
 arch/sparc64/kernel/pci.c       |    2 +
 arch/sparc64/kernel/pci_fire.c  |  725 +++++++++++++++++++++++++++++++++++++++
 arch/sparc64/kernel/pci_iommu.c |   22 +-
 arch/sparc64/kernel/prom.c      |   89 +++++-
 include/asm-sparc64/pbm.h       |    1 +
 7 files changed, 835 insertions(+), 22 deletions(-)
 create mode 100644 arch/sparc64/kernel/pci_fire.c

diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile
index eff0c01..6bf6fb6 100644
--- a/arch/sparc64/kernel/Makefile
+++ b/arch/sparc64/kernel/Makefile
@@ -17,7 +17,7 @@ obj-y         := process.o setup.o cpu.o idprom.o \
 obj-$(CONFIG_STACKTRACE) += stacktrace.o
 obj-$(CONFIG_PCI)       += ebus.o isa.o pci_common.o pci_iommu.o \
                            pci_psycho.o pci_sabre.o pci_schizo.o \
-                           pci_sun4v.o pci_sun4v_asm.o
+                           pci_sun4v.o pci_sun4v_asm.o pci_fire.o
 obj-$(CONFIG_SMP)       += smp.o trampoline.o
 obj-$(CONFIG_SPARC32_COMPAT) += sys32.o sys_sparc32.o signal32.o
 obj-$(CONFIG_BINFMT_ELF32) += binfmt_elf32.o
diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c
index c3d068c..6a73251 100644
--- a/arch/sparc64/kernel/irq.c
+++ b/arch/sparc64/kernel/irq.c
@@ -272,7 +272,7 @@ static void sun4u_irq_enable(unsigned int virt_irq)
        struct irq_handler_data *data = desc->handler_data;
 
        if (likely(data)) {
-               unsigned long cpuid, imap;
+               unsigned long cpuid, imap, val;
                unsigned int tid;
 
                cpuid = irq_choose_cpu(virt_irq);
@@ -280,7 +280,11 @@ static void sun4u_irq_enable(unsigned int virt_irq)
 
                tid = sun4u_compute_tid(imap, cpuid);
 
-               upa_writel(tid | IMAP_VALID, imap);
+               val = upa_readq(imap);
+               val &= ~(IMAP_TID_UPA | IMAP_TID_JBUS |
+                        IMAP_AID_SAFARI | IMAP_NID_SAFARI);
+               val |= tid | IMAP_VALID;
+               upa_writeq(val, imap);
        }
 }
 
@@ -291,10 +295,10 @@ static void sun4u_irq_disable(unsigned int virt_irq)
 
        if (likely(data)) {
                unsigned long imap = data->imap;
-               u32 tmp = upa_readl(imap);
+               u32 tmp = upa_readq(imap);
 
                tmp &= ~IMAP_VALID;
-               upa_writel(tmp, imap);
+               upa_writeq(tmp, imap);
        }
 }
 
@@ -304,7 +308,7 @@ static void sun4u_irq_end(unsigned int virt_irq)
        struct irq_handler_data *data = desc->handler_data;
 
        if (likely(data))
-               upa_writel(ICLR_IDLE, data->iclr);
+               upa_writeq(ICLR_IDLE, data->iclr);
 }
 
 static void sun4v_irq_enable(unsigned int virt_irq)
@@ -430,7 +434,7 @@ unsigned int build_irq(int inofixup, unsigned long iclr, 
unsigned long imap)
 
        BUG_ON(tlb_type == hypervisor);
 
-       ino = (upa_readl(imap) & (IMAP_IGN | IMAP_INO)) + inofixup;
+       ino = (upa_readq(imap) & (IMAP_IGN | IMAP_INO)) + inofixup;
        bucket = &ivector_table[ino];
        if (!bucket->virt_irq) {
                bucket->virt_irq = virt_irq_alloc(__irq(bucket));
diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c
index dfc41cd..a1a8c3e 100644
--- a/arch/sparc64/kernel/pci.c
+++ b/arch/sparc64/kernel/pci.c
@@ -183,6 +183,7 @@ extern void schizo_init(struct device_node *, const char *);
 extern void schizo_plus_init(struct device_node *, const char *);
 extern void tomatillo_init(struct device_node *, const char *);
 extern void sun4v_pci_init(struct device_node *, const char *);
+extern void fire_pci_init(struct device_node *, const char *);
 
 static struct {
        char *model_name;
@@ -200,6 +201,7 @@ static struct {
        { "SUNW,tomatillo", tomatillo_init },
        { "pci108e,a801", tomatillo_init },
        { "SUNW,sun4v-pci", sun4v_pci_init },
+       { "pciex108e,80f0", fire_pci_init },
 };
 #define PCI_NUM_CONTROLLER_TYPES (sizeof(pci_controller_table) / \
                                  sizeof(pci_controller_table[0]))
diff --git a/arch/sparc64/kernel/pci_fire.c b/arch/sparc64/kernel/pci_fire.c
new file mode 100644
index 0000000..37d33a7
--- /dev/null
+++ b/arch/sparc64/kernel/pci_fire.c
@@ -0,0 +1,725 @@
+/* pci_fire.c: Sun4u platform PCI-E controller support.
+ *
+ * Copyright (C) 2007 David S. Miller (davem@xxxxxxxxxxxxx)
+ */
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+#include <asm/pbm.h>
+#include <asm/oplib.h>
+#include <asm/prom.h>
+
+#include "pci_impl.h"
+
+#define fire_read(__reg) \
+({     u64 __ret; \
+       __asm__ __volatile__("ldxa [%1] %2, %0" \
+                            : "=r" (__ret) \
+                            : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \
+                            : "memory"); \
+       __ret; \
+})
+#define fire_write(__reg, __val) \
+       __asm__ __volatile__("stxa %0, [%1] %2" \
+                            : /* no outputs */ \
+                            : "r" (__val), "r" (__reg), \
+                              "i" (ASI_PHYS_BYPASS_EC_E) \
+                            : "memory")
+
+/* Fire config space address format is nearly identical to
+ * that of SCHIZO and PSYCHO, except that in order to accomodate
+ * PCI-E extended config space the encoding can handle 12 bits
+ * of register address:
+ *
+ *  32     28 27 20 19    15 14      12 11  2  1 0
+ * -------------------------------------------------
+ * |0 0 0 0 0| bus | device | function | reg | 0 0 |
+ * -------------------------------------------------
+ */
+#define FIRE_CONFIG_BASE(PBM)  ((PBM)->config_space)
+#define FIRE_CONFIG_ENCODE(BUS, DEVFN, REG)    \
+       (((unsigned long)(BUS)   << 20) |       \
+        ((unsigned long)(DEVFN) << 12)  |      \
+        ((unsigned long)(REG)))
+
+static void *fire_pci_config_mkaddr(struct pci_pbm_info *pbm,
+                                     unsigned char bus,
+                                     unsigned int devfn,
+                                     int where)
+{
+       if (!pbm)
+               return NULL;
+       return (void *)
+               (FIRE_CONFIG_BASE(pbm) |
+                FIRE_CONFIG_ENCODE(bus, devfn, where));
+}
+
+struct pdev_entry {
+       struct pdev_entry       *next;
+       u32                     devhandle;
+       unsigned int            bus;
+       unsigned int            device;
+       unsigned int            func;
+};
+
+#define PDEV_HTAB_SIZE 16
+#define PDEV_HTAB_MASK (PDEV_HTAB_SIZE - 1)
+static struct pdev_entry *pdev_htab[PDEV_HTAB_SIZE];
+
+static inline unsigned int pdev_hashfn(u32 devhandle, unsigned int bus, 
unsigned int device, unsigned int func)
+{
+       unsigned int val;
+
+       val = (devhandle ^ (devhandle >> 4));
+       val ^= bus;
+       val ^= device;
+       val ^= func;
+
+       return val & PDEV_HTAB_MASK;
+}
+
+static int pdev_htab_add(u32 devhandle, unsigned int bus, unsigned int device, 
unsigned int func)
+{
+       struct pdev_entry *p = kmalloc(sizeof(*p), GFP_KERNEL);
+       struct pdev_entry **slot;
+
+       if (!p)
+               return -ENOMEM;
+
+       slot = &pdev_htab[pdev_hashfn(devhandle, bus, device, func)];
+       p->next = *slot;
+       *slot = p;
+
+       p->devhandle = devhandle;
+       p->bus = bus;
+       p->device = device;
+       p->func = func;
+
+       return 0;
+}
+
+/* Recursively descend into the OBP device tree, rooted at toplevel_node,
+ * looking for a PCI device matching bus and devfn.
+ */
+static int obp_find(struct device_node *toplevel_node, unsigned int bus, 
unsigned int devfn)
+{
+       toplevel_node = toplevel_node->child;
+
+       while (toplevel_node != NULL) {
+               struct linux_prom_pci_registers *regs;
+               struct property *prop;
+               int ret;
+
+               ret = obp_find(toplevel_node, bus, devfn);
+               if (ret != 0)
+                       return ret;
+
+               prop = of_find_property(toplevel_node, "reg", NULL);
+               if (!prop)
+                       goto next_sibling;
+
+               regs = prop->value;
+               if (((regs->phys_hi >> 16) & 0xff) == bus &&
+                   ((regs->phys_hi >> 8) & 0xff) == devfn)
+                       break;
+
+       next_sibling:
+               toplevel_node = toplevel_node->sibling;
+       }
+
+       return toplevel_node != NULL;
+}
+
+static int pdev_htab_populate(struct pci_pbm_info *pbm)
+{
+       u32 devhandle = pbm->devhandle;
+       unsigned int bus;
+
+       for (bus = pbm->pci_first_busno; bus <= pbm->pci_last_busno; bus++) {
+               unsigned int devfn;
+
+               for (devfn = 0; devfn < 256; devfn++) {
+                       unsigned int device = PCI_SLOT(devfn);
+                       unsigned int func = PCI_FUNC(devfn);
+
+                       if (obp_find(pbm->prom_node, bus, devfn)) {
+                               int err = pdev_htab_add(devhandle, bus,
+                                                       device, func);
+                               if (err)
+                                       return err;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static struct pdev_entry *pdev_find(u32 devhandle, unsigned int bus, unsigned 
int device, unsigned int func)
+{
+       struct pdev_entry *p;
+
+       p = pdev_htab[pdev_hashfn(devhandle, bus, device, func)];
+       while (p) {
+               if (p->devhandle == devhandle &&
+                   p->bus == bus &&
+                   p->device == device &&
+                   p->func == func)
+                       break;
+
+               p = p->next;
+       }
+
+       return p;
+}
+
+static int fire_out_of_range(struct pci_pbm_info *pbm,
+                            unsigned char bus,
+                            unsigned char devfn)
+{
+       if (bus < pbm->pci_first_busno ||
+           bus > pbm->pci_last_busno)
+               return 1;
+       return pdev_find(pbm->devhandle, bus,
+                        PCI_SLOT(devfn), PCI_FUNC(devfn)) == NULL;
+}
+
+/* FIRE PCI configuration space accessors. */
+
+static int fire_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
+                            int where, int size, u32 *value)
+{
+       struct pci_pbm_info *pbm = bus_dev->sysdata;
+       unsigned char bus = bus_dev->number;
+       u32 *addr;
+       u16 tmp16;
+       u8 tmp8;
+
+       switch (size) {
+       case 1:
+               *value = 0xff;
+               break;
+       case 2:
+               *value = 0xffff;
+               break;
+       case 4:
+               *value = 0xffffffff;
+               break;
+       }
+
+       addr = fire_pci_config_mkaddr(pbm, bus, devfn, where);
+       if (!addr)
+               return PCIBIOS_SUCCESSFUL;
+
+       if (fire_out_of_range(pbm, bus, devfn))
+               return PCIBIOS_SUCCESSFUL;
+
+       switch (size) {
+       case 1:
+               pci_config_read8((u8 *)addr, &tmp8);
+               *value = tmp8;
+               break;
+
+       case 2:
+               if (where & 0x01) {
+                       printk("pci_read_config_word: misaligned reg [%x]\n",
+                              where);
+                       return PCIBIOS_SUCCESSFUL;
+               }
+               pci_config_read16((u16 *)addr, &tmp16);
+               *value = tmp16;
+               break;
+
+       case 4:
+               if (where & 0x03) {
+                       printk("pci_read_config_dword: misaligned reg [%x]\n",
+                              where);
+                       return PCIBIOS_SUCCESSFUL;
+               }
+
+               pci_config_read32(addr, value);
+               break;
+       }
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static int fire_write_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
+                             int where, int size, u32 value)
+{
+       struct pci_pbm_info *pbm = bus_dev->sysdata;
+       unsigned char bus = bus_dev->number;
+       u32 *addr;
+
+       addr = fire_pci_config_mkaddr(pbm, bus, devfn, where);
+       if (!addr)
+               return PCIBIOS_SUCCESSFUL;
+
+       if (fire_out_of_range(pbm, bus, devfn))
+               return PCIBIOS_SUCCESSFUL;
+
+       switch (size) {
+       case 1:
+               pci_config_write8((u8 *)addr, value);
+               break;
+
+       case 2:
+               if (where & 0x01) {
+                       printk("pci_write_config_word: misaligned reg [%x]\n",
+                              where);
+                       return PCIBIOS_SUCCESSFUL;
+               }
+               pci_config_write16((u16 *)addr, value);
+               break;
+
+       case 4:
+               if (where & 0x03) {
+                       printk("pci_write_config_dword: misaligned reg [%x]\n",
+                              where);
+                       return PCIBIOS_SUCCESSFUL;
+               }
+
+               pci_config_write32(addr, value);
+       }
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops pci_fire_ops = {
+       .read   =       fire_read_pci_cfg,
+       .write  =       fire_write_pci_cfg,
+};
+
+static void pbm_scan_bus(struct pci_controller_info *p,
+                        struct pci_pbm_info *pbm)
+{
+       struct pcidev_cookie *cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
+
+       if (!cookie) {
+               prom_printf("%s: Critical allocation failure.\n", pbm->name);
+               prom_halt();
+       }
+
+       /* All we care about is the PBM. */
+       cookie->pbm = pbm;
+
+       pbm->pci_bus = pci_scan_bus(pbm->pci_first_busno, p->pci_ops, pbm);
+#if 0
+       pci_fixup_host_bridge_self(pbm->pci_bus);
+       pbm->pci_bus->self->sysdata = cookie;
+#endif
+       pci_fill_in_pbm_cookies(pbm->pci_bus, pbm, pbm->prom_node);
+       pci_record_assignments(pbm, pbm->pci_bus);
+       pci_assign_unassigned(pbm, pbm->pci_bus);
+       pci_fixup_irq(pbm, pbm->pci_bus);
+       pci_determine_66mhz_disposition(pbm, pbm->pci_bus);
+       pci_setup_busmastering(pbm, pbm->pci_bus);
+}
+
+static void pci_fire_scan_bus(struct pci_controller_info *p)
+{
+       struct device_node *dp;
+       struct property *prop;
+
+       if ((dp = p->pbm_A.prom_node) != NULL) {
+               prop = of_find_property(dp, "66mhz-capable", NULL);
+               p->pbm_A.is_66mhz_capable = (prop != NULL);
+
+               pbm_scan_bus(p, &p->pbm_A);
+       }
+
+       if ((dp = p->pbm_B.prom_node) != NULL) {
+               prop = of_find_property(dp, "66mhz-capable", NULL);
+               p->pbm_B.is_66mhz_capable = (prop != NULL);
+
+               pbm_scan_bus(p, &p->pbm_B);
+       }
+
+       /* XXX register error interrupt handlers XXX */
+}
+
+static void pci_fire_base_address_update(struct pci_dev *pdev, int resource)
+{
+       struct pcidev_cookie *pcp = pdev->sysdata;
+       struct pci_pbm_info *pbm = pcp->pbm;
+       struct resource *res, *root;
+       u32 reg;
+       int where, size, is_64bit;
+
+       res = &pdev->resource[resource];
+       if (resource < 6) {
+               where = PCI_BASE_ADDRESS_0 + (resource * 4);
+       } else if (resource == PCI_ROM_RESOURCE) {
+               where = pdev->rom_base_reg;
+       } else {
+               /* Somebody might have asked allocation of a non-standard 
resource */
+               return;
+       }
+
+       /* XXX 64-bit MEM handling is not %100 correct... XXX */
+       is_64bit = 0;
+       if (res->flags & IORESOURCE_IO)
+               root = &pbm->io_space;
+       else {
+               root = &pbm->mem_space;
+               if ((res->flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK)
+                   == PCI_BASE_ADDRESS_MEM_TYPE_64)
+                       is_64bit = 1;
+       }
+
+       size = res->end - res->start;
+       pci_read_config_dword(pdev, where, &reg);
+       reg = ((reg & size) |
+              (((u32)(res->start - root->start)) & ~size));
+       if (resource == PCI_ROM_RESOURCE) {
+               reg |= PCI_ROM_ADDRESS_ENABLE;
+               res->flags |= IORESOURCE_ROM_ENABLE;
+       }
+       pci_write_config_dword(pdev, where, reg);
+
+       /* This knows that the upper 32-bits of the address
+        * must be zero.  Our PCI common layer enforces this.
+        */
+       if (is_64bit)
+               pci_write_config_dword(pdev, where + 4, 0);
+}
+
+static void pci_fire_resource_adjust(struct pci_dev *pdev,
+                                    struct resource *res,
+                                    struct resource *root)
+{
+       res->start += root->start;
+       res->end += root->start;
+}
+
+/* Use ranges property to determine where PCI MEM, I/O, and Config
+ * space are for this PCI bus module.
+ */
+static void pci_fire_determine_mem_io_space(struct pci_pbm_info *pbm)
+{
+       int i, saw_mem, saw_io, saw_cfg;
+
+       saw_cfg = saw_mem = saw_io = 0;
+       for (i = 0; i < pbm->num_pbm_ranges; i++) {
+               struct linux_prom_pci_ranges *pr = &pbm->pbm_ranges[i];
+               unsigned long a;
+               int type;
+
+               type = (pr->child_phys_hi >> 24) & 0x3;
+               a = (((unsigned long)pr->parent_phys_hi << 32UL) |
+                    ((unsigned long)pr->parent_phys_lo  <<  0UL));
+
+               switch (type) {
+               case 0:
+                       /* PCI config space, 16MB */
+                       pbm->config_space = a;
+                       saw_cfg = 1;
+                       break;
+
+               case 1:
+                       /* 16-bit IO space, 16MB */
+                       pbm->io_space.start = a;
+                       pbm->io_space.end = a + ((16UL*1024UL*1024UL) - 1UL);
+                       pbm->io_space.flags = IORESOURCE_IO;
+                       saw_io = 1;
+                       break;
+
+               case 2:
+                       /* 32-bit MEM space, 2GB */
+                       pbm->mem_space.start = a;
+                       pbm->mem_space.end = a + (0x80000000UL - 1UL);
+                       pbm->mem_space.flags = IORESOURCE_MEM;
+                       saw_mem = 1;
+                       break;
+
+               case 3:
+                       /* XXX 64-bit MEM handling XXX */
+
+               default:
+                       break;
+               };
+       }
+
+       if (!saw_cfg || !saw_io || !saw_mem) {
+               prom_printf("%s: Fatal error, missing %s PBM range.\n",
+                           pbm->name,
+                           (!saw_io ? "IO" : "MEM"));
+               prom_halt();
+       }
+
+       printk("%s: PCI CFG[%lx] IO[%lx] MEM[%lx]\n",
+              pbm->name,
+              pbm->config_space,
+              pbm->io_space.start,
+              pbm->mem_space.start);
+}
+
+static void pbm_register_toplevel_resources(struct pci_controller_info *p,
+                                           struct pci_pbm_info *pbm)
+{
+       pbm->io_space.name = pbm->mem_space.name = pbm->name;
+
+       request_resource(&ioport_resource, &pbm->io_space);
+       request_resource(&iomem_resource, &pbm->mem_space);
+       pci_register_legacy_regions(&pbm->io_space,
+                                   &pbm->mem_space);
+}
+
+#define FIRE_IOMMU_CONTROL     0x40000UL
+#define FIRE_IOMMU_TSBBASE     0x40008UL
+#define FIRE_IOMMU_FLUSH       0x40100UL
+#define FIRE_IOMMU_FLUSHINV    0x40100UL
+
+static void pci_fire_pbm_iommu_init(struct pci_pbm_info *pbm)
+{
+       struct pci_iommu *iommu = pbm->iommu;
+       u32 vdma[2], dma_mask;
+       u64 control;
+       int tsbsize;
+
+       /* No virtual-dma property on these guys, use largest size.  */
+       vdma[0] = 0xc0000000; /* base */
+       vdma[1] = 0x40000000; /* size */
+       dma_mask = 0xffffffff;
+       tsbsize = 128;
+
+       /* Register addresses. */
+       iommu->iommu_control  = pbm->pbm_regs + FIRE_IOMMU_CONTROL;
+       iommu->iommu_tsbbase  = pbm->pbm_regs + FIRE_IOMMU_TSBBASE;
+       iommu->iommu_flush    = pbm->pbm_regs + FIRE_IOMMU_FLUSH;
+       iommu->iommu_flushinv = pbm->pbm_regs + FIRE_IOMMU_FLUSHINV;
+
+       /* We use the main control/status register of FIRE as the write
+        * completion register.
+        */
+       iommu->write_complete_reg = pbm->controller_regs + 0x410000UL;
+
+       /*
+        * Invalidate TLB Entries.
+        */
+       fire_write(iommu->iommu_flushinv, ~(u64)0);
+
+       pci_iommu_table_init(iommu, tsbsize * 8 * 1024, vdma[0], dma_mask);
+
+       fire_write(iommu->iommu_tsbbase, __pa(iommu->page_table) | 0x7UL);
+
+       control = fire_read(iommu->iommu_control);
+       control |= (0x00000400 /* TSB cache snoop enable */     |
+                   0x00000300 /* Cache mode */                 |
+                   0x00000002 /* Bypass enable */              |
+                   0x00000001 /* Translation enable */);
+       fire_write(iommu->iommu_control, control);
+}
+
+/* Based at pbm->controller_regs */
+#define FIRE_PARITY_CONTROL    0x470010UL
+#define  FIRE_PARITY_ENAB      0x8000000000000000UL
+#define FIRE_FATAL_RESET_CTL   0x471028UL
+#define  FIRE_FATAL_RESET_SPARE        0x0000000004000000UL
+#define  FIRE_FATAL_RESET_MB   0x0000000002000000UL
+#define  FIRE_FATAL_RESET_CPE  0x0000000000008000UL
+#define  FIRE_FATAL_RESET_APE  0x0000000000004000UL
+#define  FIRE_FATAL_RESET_PIO  0x0000000000000040UL
+#define  FIRE_FATAL_RESET_JW   0x0000000000000004UL
+#define  FIRE_FATAL_RESET_JI   0x0000000000000002UL
+#define  FIRE_FATAL_RESET_JR   0x0000000000000001UL
+#define FIRE_CORE_INTR_ENABLE  0x471800UL
+
+/* Based at pbm->pbm_regs */
+#define FIRE_TLU_CTRL          0x80000UL
+#define  FIRE_TLU_CTRL_TIM     0x00000000da000000UL
+#define  FIRE_TLU_CTRL_QDET    0x0000000000000100UL
+#define  FIRE_TLU_CTRL_CFG     0x0000000000000001UL
+#define FIRE_TLU_DEV_CTRL      0x90008UL
+#define FIRE_TLU_LINK_CTRL     0x90020UL
+#define FIRE_TLU_LINK_CTRL_CLK 0x0000000000000040UL
+#define FIRE_LPU_RESET         0xe2008UL
+#define FIRE_LPU_LLCFG         0xe2200UL
+#define  FIRE_LPU_LLCFG_VC0    0x0000000000000100UL
+#define FIRE_LPU_FCTRL_UCTRL   0xe2240UL
+#define  FIRE_LPU_FCTRL_UCTRL_N        0x0000000000000002UL
+#define  FIRE_LPU_FCTRL_UCTRL_P        0x0000000000000001UL
+#define FIRE_LPU_TXL_FIFOP     0xe2430UL
+#define FIRE_LPU_LTSSM_CFG2    0xe2788UL
+#define FIRE_LPU_LTSSM_CFG3    0xe2790UL
+#define FIRE_LPU_LTSSM_CFG4    0xe2798UL
+#define FIRE_LPU_LTSSM_CFG5    0xe27a0UL
+#define FIRE_DMC_IENAB         0x31800UL
+#define FIRE_DMC_DBG_SEL_A     0x53000UL
+#define FIRE_DMC_DBG_SEL_B     0x53008UL
+#define FIRE_PEC_IENAB         0x51800UL
+
+static void pci_fire_hw_init(struct pci_pbm_info *pbm)
+{
+       u64 val;
+
+       fire_write(pbm->controller_regs + FIRE_PARITY_CONTROL,
+                  FIRE_PARITY_ENAB);
+
+       fire_write(pbm->controller_regs + FIRE_FATAL_RESET_CTL,
+                  (FIRE_FATAL_RESET_SPARE |
+                   FIRE_FATAL_RESET_MB |
+                   FIRE_FATAL_RESET_CPE |
+                   FIRE_FATAL_RESET_APE |
+                   FIRE_FATAL_RESET_PIO |
+                   FIRE_FATAL_RESET_JW |
+                   FIRE_FATAL_RESET_JI |
+                   FIRE_FATAL_RESET_JR));
+
+       fire_write(pbm->controller_regs + FIRE_CORE_INTR_ENABLE, ~(u64)0);
+
+       val = fire_read(pbm->pbm_regs + FIRE_TLU_CTRL);
+       val |= (FIRE_TLU_CTRL_TIM |
+               FIRE_TLU_CTRL_QDET |
+               FIRE_TLU_CTRL_CFG);
+       fire_write(pbm->pbm_regs + FIRE_TLU_CTRL, val);
+       fire_write(pbm->pbm_regs + FIRE_TLU_DEV_CTRL, 0);
+       fire_write(pbm->pbm_regs + FIRE_TLU_LINK_CTRL,
+                  FIRE_TLU_LINK_CTRL_CLK);
+
+       fire_write(pbm->pbm_regs + FIRE_LPU_RESET, 0);
+       fire_write(pbm->pbm_regs + FIRE_LPU_LLCFG,
+                  FIRE_LPU_LLCFG_VC0);
+       fire_write(pbm->pbm_regs + FIRE_LPU_FCTRL_UCTRL,
+                  (FIRE_LPU_FCTRL_UCTRL_N |
+                   FIRE_LPU_FCTRL_UCTRL_P));
+       fire_write(pbm->pbm_regs + FIRE_LPU_TXL_FIFOP,
+                  ((0xffff << 16) | (0x0000 << 0)));
+       fire_write(pbm->pbm_regs + FIRE_LPU_LTSSM_CFG2, 3000000);
+       fire_write(pbm->pbm_regs + FIRE_LPU_LTSSM_CFG3, 500000);
+       fire_write(pbm->pbm_regs + FIRE_LPU_LTSSM_CFG4,
+                  (2 << 16) | (140 << 8));
+       fire_write(pbm->pbm_regs + FIRE_LPU_LTSSM_CFG5, 0);
+
+       fire_write(pbm->pbm_regs + FIRE_DMC_IENAB, ~(u64)0);
+       fire_write(pbm->pbm_regs + FIRE_DMC_DBG_SEL_A, 0);
+       fire_write(pbm->pbm_regs + FIRE_DMC_DBG_SEL_B, 0);
+
+       fire_write(pbm->pbm_regs + FIRE_PEC_IENAB, ~(u64)0);
+}
+
+static void pci_fire_pbm_init(struct pci_controller_info *p,
+                               struct device_node *dp, u32 portid)
+{
+       const struct linux_prom64_registers *regs;
+       struct pci_pbm_info *pbm;
+       const u32 *ino_bitmap;
+       struct property *prop;
+       const unsigned int *busrange;
+       int len;
+
+       if ((portid & 1) == 0)
+               pbm = &p->pbm_A;
+       else
+               pbm = &p->pbm_B;
+
+       pbm->portid = portid;
+       pbm->parent = p;
+       pbm->prom_node = dp;
+       pbm->pci_first_slot = 1;
+
+       pbm->name = dp->full_name;
+
+       printk("%s: SUN4U PCIE Bus Module\n", pbm->name);
+
+       regs = of_get_property(dp, "reg", NULL);
+       pbm->pbm_regs = regs[0].phys_addr;
+       pbm->controller_regs = regs[1].phys_addr - 0x410000UL;
+
+       prop = of_find_property(dp, "ranges", &len);
+       pbm->pbm_ranges = prop->value;
+       pbm->num_pbm_ranges =
+               (len / sizeof(struct linux_prom_pci_ranges));
+
+       pci_fire_determine_mem_io_space(pbm);
+       pbm_register_toplevel_resources(p, pbm);
+
+       prop = of_find_property(dp, "interrupt-map", &len);
+       pbm->pbm_intmap = prop->value;
+       pbm->num_pbm_intmap =
+               (len / sizeof(struct linux_prom_pci_intmap));
+
+       prop = of_find_property(dp, "interrupt-map-mask", NULL);
+       pbm->pbm_intmask = prop->value;
+
+       ino_bitmap = of_get_property(dp, "ino-bitmap", NULL);
+       pbm->ino_bitmap = (((u64)ino_bitmap[1] << 32UL) |
+                          ((u64)ino_bitmap[0] <<  0UL));
+
+       busrange = of_get_property(dp, "bus-range", NULL);
+       pbm->pci_first_busno = busrange[0];
+       pbm->pci_last_busno = busrange[1];
+
+       pci_fire_hw_init(pbm);
+       pci_fire_pbm_iommu_init(pbm);
+
+       pdev_htab_populate(pbm);
+}
+
+static inline int portid_compare(u32 x, u32 y)
+{
+       if (x == (y ^ 1))
+               return 1;
+       return 0;
+}
+
+void fire_pci_init(struct device_node *dp, const char *model_name)
+{
+       struct pci_controller_info *p;
+       u32 portid = of_getintprop_default(dp, "portid", 0xff);
+       struct pci_iommu *iommu;
+
+       for (p = pci_controller_root; p; p = p->next) {
+               struct pci_pbm_info *pbm;
+
+               if (p->pbm_A.prom_node && p->pbm_B.prom_node)
+                       continue;
+
+               pbm = (p->pbm_A.prom_node ?
+                      &p->pbm_A :
+                      &p->pbm_B);
+
+               if (portid_compare(pbm->portid, portid)) {
+                       pci_fire_pbm_init(p, dp, portid);
+                       return;
+               }
+       }
+
+       p = kzalloc(sizeof(struct pci_controller_info), GFP_ATOMIC);
+       if (!p)
+               goto fatal_memory_error;
+
+       iommu = kzalloc(sizeof(struct pci_iommu), GFP_ATOMIC);
+       if (!iommu)
+               goto fatal_memory_error;
+
+       p->pbm_A.iommu = iommu;
+
+       iommu = kzalloc(sizeof(struct pci_iommu), GFP_ATOMIC);
+       if (!iommu)
+               goto fatal_memory_error;
+
+       p->pbm_B.iommu = iommu;
+
+       p->next = pci_controller_root;
+       pci_controller_root = p;
+
+       p->index = pci_num_controllers++;
+       p->pbms_same_domain = 0;
+       p->scan_bus = pci_fire_scan_bus;
+       p->base_address_update = pci_fire_base_address_update;
+       p->resource_adjust = pci_fire_resource_adjust;
+       /* XXX MSI support XXX */
+       p->pci_ops = &pci_fire_ops;
+
+       /* Like PSYCHO and SCHIZO we have a 2GB aligned area
+        * for memory space.
+        */
+       pci_memspace_mask = 0x7fffffffUL;
+
+       pci_fire_pbm_init(p, dp, portid);
+       return;
+
+fatal_memory_error:
+       prom_printf("PCI_FIRE: Fatal memory allocation error.\n");
+       prom_halt();
+}
diff --git a/arch/sparc64/kernel/pci_iommu.c b/arch/sparc64/kernel/pci_iommu.c
index 2e7f142..10477ee 100644
--- a/arch/sparc64/kernel/pci_iommu.c
+++ b/arch/sparc64/kernel/pci_iommu.c
@@ -38,17 +38,21 @@
 /* Must be invoked under the IOMMU lock. */
 static void __iommu_flushall(struct pci_iommu *iommu)
 {
-       unsigned long tag;
-       int entry;
+       if (iommu->iommu_flushinv) {
+               pci_iommu_write(iommu->iommu_flushinv, ~(u64)0);
+       } else {
+               unsigned long tag;
+               int entry;
 
-       tag = iommu->iommu_flush + (0xa580UL - 0x0210UL);
-       for (entry = 0; entry < 16; entry++) {
-               pci_iommu_write(tag, 0);
-               tag += 8;
-       }
+               tag = iommu->iommu_flush + (0xa580UL - 0x0210UL);
+               for (entry = 0; entry < 16; entry++) {
+                       pci_iommu_write(tag, 0);
+                       tag += 8;
+               }
 
-       /* Ensure completion of previous PIO writes. */
-       (void) pci_iommu_read(iommu->write_complete_reg);
+               /* Ensure completion of previous PIO writes. */
+               (void) pci_iommu_read(iommu->write_complete_reg);
+       }
 }
 
 #define IOPTE_CONSISTENT(CTX) \
diff --git a/arch/sparc64/kernel/prom.c b/arch/sparc64/kernel/prom.c
index 0917c24..e6bc55a 100644
--- a/arch/sparc64/kernel/prom.c
+++ b/arch/sparc64/kernel/prom.c
@@ -383,11 +383,9 @@ static unsigned int psycho_irq_build(struct device_node 
*dp,
 
        /* Now build the IRQ bucket. */
        imap = controller_regs + imap_off;
-       imap += 4;
 
        iclr_off = psycho_iclr_offset(ino);
        iclr = controller_regs + iclr_off;
-       iclr += 4;
 
        if ((ino & 0x20) == 0)
                inofixup = ino & 0x03;
@@ -610,11 +608,9 @@ static unsigned int sabre_irq_build(struct device_node *dp,
 
        /* Now build the IRQ bucket. */
        imap = controller_regs + imap_off;
-       imap += 4;
 
        iclr_off = sabre_iclr_offset(ino);
        iclr = controller_regs + iclr_off;
-       iclr += 4;
 
        if ((ino & 0x20) == 0)
                inofixup = ino & 0x03;
@@ -676,13 +672,14 @@ static unsigned long schizo_iclr_offset(unsigned long ino)
 static unsigned long schizo_ino_to_iclr(unsigned long pbm_regs,
                                        unsigned int ino)
 {
-       return pbm_regs + schizo_iclr_offset(ino) + 4;
+
+       return pbm_regs + schizo_iclr_offset(ino);
 }
 
 static unsigned long schizo_ino_to_imap(unsigned long pbm_regs,
                                        unsigned int ino)
 {
-       return pbm_regs + schizo_imap_offset(ino) + 4;
+       return pbm_regs + schizo_imap_offset(ino);
 }
 
 #define schizo_read(__reg) \
@@ -845,6 +842,85 @@ static void pci_sun4v_irq_trans_init(struct device_node 
*dp)
        dp->irq_trans->data = (void *) (unsigned long)
                ((regs->phys_addr >> 32UL) & 0x0fffffff);
 }
+
+struct fire_irq_data {
+       unsigned long pbm_regs;
+       u32 portid;
+};
+
+#define FIRE_IMAP_BASE 0x001000
+#define FIRE_ICLR_BASE 0x001400
+
+static unsigned long fire_imap_offset(unsigned long ino)
+{
+       return FIRE_IMAP_BASE + (ino * 8UL);
+}
+
+static unsigned long fire_iclr_offset(unsigned long ino)
+{
+       return FIRE_ICLR_BASE + (ino * 8UL);
+}
+
+static unsigned long fire_ino_to_iclr(unsigned long pbm_regs,
+                                           unsigned int ino)
+{
+       return pbm_regs + fire_iclr_offset(ino);
+}
+
+static unsigned long fire_ino_to_imap(unsigned long pbm_regs,
+                                           unsigned int ino)
+{
+       return pbm_regs + fire_imap_offset(ino);
+}
+
+static unsigned int fire_irq_build(struct device_node *dp,
+                                        unsigned int ino,
+                                        void *_data)
+{
+       struct fire_irq_data *irq_data = _data;
+       unsigned long pbm_regs = irq_data->pbm_regs;
+       unsigned long imap, iclr;
+       unsigned long int_ctrlr;
+
+       ino &= 0x3f;
+
+       /* Now build the IRQ bucket. */
+       imap = fire_ino_to_imap(pbm_regs, ino);
+       iclr = fire_ino_to_iclr(pbm_regs, ino);
+
+       /* Set the interrupt controller number.  */
+       int_ctrlr = 1 << 6;
+       upa_writeq(int_ctrlr, imap);
+
+       /* The interrupt map registers do not have an INO field
+        * like other chips do.  They return zero in the INO
+        * field, and the interrupt controller number is controlled
+        * in bits 6 thru 9.  So in order for build_irq() to get
+        * the INO right we pass it in as part of the fixup
+        * which will get added to the map register zero value
+        * read by build_irq().
+        */
+       ino |= (irq_data->portid << 6);
+       ino -= int_ctrlr;
+       return build_irq(ino, iclr, imap);
+}
+
+static void fire_irq_trans_init(struct device_node *dp)
+{
+       const struct linux_prom64_registers *regs;
+       struct fire_irq_data *irq_data;
+
+       dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
+       dp->irq_trans->irq_build = fire_irq_build;
+
+       irq_data = prom_early_alloc(sizeof(struct fire_irq_data));
+
+       regs = of_get_property(dp, "reg", NULL);
+       dp->irq_trans->data = irq_data;
+
+       irq_data->pbm_regs = regs[0].phys_addr;
+       irq_data->portid = of_getintprop_default(dp, "portid", 0);
+}
 #endif /* CONFIG_PCI */
 
 #ifdef CONFIG_SBUS
@@ -1066,6 +1142,7 @@ static struct irq_trans pci_irq_trans_table[] = {
        { "SUNW,tomatillo", tomatillo_irq_trans_init },
        { "pci108e,a801", tomatillo_irq_trans_init },
        { "SUNW,sun4v-pci", pci_sun4v_irq_trans_init },
+       { "pciex108e,80f0", fire_irq_trans_init },
 };
 #endif
 
diff --git a/include/asm-sparc64/pbm.h b/include/asm-sparc64/pbm.h
index dcfa762..3ee9c80 100644
--- a/include/asm-sparc64/pbm.h
+++ b/include/asm-sparc64/pbm.h
@@ -60,6 +60,7 @@ struct pci_iommu {
        unsigned long   iommu_control;          /* IOMMU control register */
        unsigned long   iommu_tsbbase;          /* IOMMU page table base 
register */
        unsigned long   iommu_flush;            /* IOMMU page flush register */
+       unsigned long   iommu_flushinv;         /* IOMMU page flushinv register 
*/
        unsigned long   iommu_ctxflush;         /* IOMMU context flush register 
*/
 
        /* This is a register in the PCI controller, which if
-- 
1.5.1.3

>From 694bd892868cbf574b66f0db14926f955e9e3fdc Mon Sep 17 00:00:00 2001
From: David S. Miller <davem@xxxxxxxxxxxxxxxxxxxx>
Date: Mon, 7 May 2007 17:12:46 -0700
Subject: [PATCH] [VIDEO]: Add Sun XVR-500 framebuffer driver.

Signed-off-by: David S. Miller <davem@xxxxxxxxxxxxx>
---
 drivers/video/Kconfig     |   13 ++
 drivers/video/Makefile    |    1 +
 drivers/video/sunxvr500.c |  445 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 459 insertions(+), 0 deletions(-)
 create mode 100644 drivers/video/sunxvr500.c

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index db373cf..c5dfe17 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1409,6 +1409,19 @@ config FB_LEO
          This is the frame buffer device driver for the SBUS-based Sun ZX
          (leo) frame buffer cards.
 
+config FB_XVR500
+       bool "Sun XVR-500 3DLABS Wildcat support"
+       depends on FB && PCI && SPARC64
+       select FB_CFB_FILLRECT
+       select FB_CFB_COPYAREA
+       select FB_CFB_IMAGEBLIT
+       help
+         This is the framebuffer device for the Sun XVR-500 and similar
+         graphics cards based upon the 3DLABS Wildcat chipset.  The driver
+         only works on sparc64 systems where the system firwmare has
+         mostly initialized the card already.  It is treated as a
+         completely dumb framebuffer device.
+
 config FB_PCI
        bool "PCI framebuffers"
        depends on (FB = y) && PCI && SPARC
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index a2481d8..b2cdd35 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -68,6 +68,7 @@ obj-$(CONFIG_FB_ACORN)            += acornfb.o
 obj-$(CONFIG_FB_ATARI)            += atafb.o
 obj-$(CONFIG_FB_MAC)              += macfb.o
 obj-$(CONFIG_FB_HGA)              += hgafb.o
+obj-$(CONFIG_FB_XVR500)           += sunxvr500.o
 obj-$(CONFIG_FB_IGA)              += igafb.o
 obj-$(CONFIG_FB_APOLLO)           += dnfb.o
 obj-$(CONFIG_FB_Q40)              += q40fb.o
diff --git a/drivers/video/sunxvr500.c b/drivers/video/sunxvr500.c
new file mode 100644
index 0000000..fa776a3
--- /dev/null
+++ b/drivers/video/sunxvr500.c
@@ -0,0 +1,445 @@
+/* sunxvr500.c: Sun 3DLABS XVR-500 Expert3D driver for sparc64 systems
+ *
+ * Copyright (C) 2007 David S. Miller (davem@xxxxxxxxxxxxx)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/of_device.h>
+#include <asm/pbm.h>
+
+/* XXX This device has a 'dev-comm' property which aparently is
+ * XXX a pointer into the openfirmware's address space which is
+ * XXX a shared area the kernel driver can use to keep OBP
+ * XXX informed about the current resolution setting.  The idea
+ * XXX is that the kernel can change resolutions, and as long
+ * XXX as the values in the 'dev-comm' area are accurate then
+ * XXX OBP can still render text properly to the console.
+ * XXX
+ * XXX I'm still working out the layout of this and whether there
+ * XXX are any signatures we need to look for etc.
+ */
+struct e3d_info {
+       struct fb_info          *info;
+       struct pci_dev          *pdev;
+
+       spinlock_t              lock;
+
+       char __iomem            *fb_base;
+       unsigned long           fb_base_phys;
+
+       unsigned long           fb8_buf_diff;
+       unsigned long           regs_base_phys;
+
+       void __iomem            *ramdac;
+
+       struct device_node      *of_node;
+
+       unsigned int            width;
+       unsigned int            height;
+       unsigned int            depth;
+       unsigned int            fb_size;
+
+       u32                     fb_base_reg;
+       u32                     fb8_0_off;
+       u32                     fb8_1_off;
+
+       u32                     pseudo_palette[256];
+};
+
+static int __devinit e3d_get_props(struct e3d_info *ep)
+{
+       ep->width = of_getintprop_default(ep->of_node, "width", 0);
+       ep->height = of_getintprop_default(ep->of_node, "height", 0);
+       ep->depth = of_getintprop_default(ep->of_node, "depth", 8);
+
+       if (!ep->width || !ep->height) {
+               printk(KERN_ERR "e3d: Critical properties missing for %s\n",
+                      pci_name(ep->pdev));
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/* My XVR-500 comes up, at 1280x768 and a FB base register value of
+ * 0x04000000, the following video layout register values:
+ *
+ * RAMDAC_VID_WH       0x03ff04ff
+ * RAMDAC_VID_CFG      0x1a0b0088
+ * RAMDAC_VID_32FB_0   0x04000000
+ * RAMDAC_VID_32FB_1   0x04800000
+ * RAMDAC_VID_8FB_0    0x05000000
+ * RAMDAC_VID_8FB_1    0x05200000
+ * RAMDAC_VID_XXXFB    0x05400000
+ * RAMDAC_VID_YYYFB    0x05c00000
+ * RAMDAC_VID_ZZZFB    0x05e00000
+ */
+/* Video layout registers */
+#define RAMDAC_VID_WH          0x00000070UL /* (height-1)<<16 | (width-1) */
+#define RAMDAC_VID_CFG         0x00000074UL /* 0x1a000088|(linesz_log2<<16) */
+#define RAMDAC_VID_32FB_0      0x00000078UL /* PCI base 32bpp FB buffer 0 */
+#define RAMDAC_VID_32FB_1      0x0000007cUL /* PCI base 32bpp FB buffer 1 */
+#define RAMDAC_VID_8FB_0       0x00000080UL /* PCI base 8bpp FB buffer 0 */
+#define RAMDAC_VID_8FB_1       0x00000084UL /* PCI base 8bpp FB buffer 1 */
+#define RAMDAC_VID_XXXFB       0x00000088UL /* PCI base of XXX FB */
+#define RAMDAC_VID_YYYFB       0x0000008cUL /* PCI base of YYY FB */
+#define RAMDAC_VID_ZZZFB       0x00000090UL /* PCI base of ZZZ FB */
+
+/* CLUT registers */
+#define RAMDAC_INDEX           0x000000bcUL
+#define RAMDAC_DATA            0x000000c0UL
+
+static void e3d_clut_write(struct e3d_info *ep, int index, u32 val)
+{
+       void __iomem *ramdac = ep->ramdac;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ep->lock, flags);
+
+       writel(index, ramdac + RAMDAC_INDEX);
+       writel(val, ramdac + RAMDAC_DATA);
+
+       spin_unlock_irqrestore(&ep->lock, flags);
+}
+
+static int e3d_setcolreg(unsigned regno,
+                        unsigned red, unsigned green, unsigned blue,
+                        unsigned transp, struct fb_info *info)
+{
+       struct e3d_info *ep = info->par;
+       u32 red_8, green_8, blue_8;
+       u32 red_10, green_10, blue_10;
+       u32 value;
+
+       if (regno >= 256)
+               return 1;
+
+       red_8 = red >> 8;
+       green_8 = green >> 8;
+       blue_8 = blue >> 8;
+
+       value = (blue_8 << 24) | (green_8 << 16) | (red_8 << 8);
+       ((u32 *)info->pseudo_palette)[regno] = value;
+
+
+       red_10 = red >> 6;
+       green_10 = green >> 6;
+       blue_10 = blue >> 6;
+
+       value = (blue_10 << 20) | (green_10 << 10) | (red_10 << 0);
+       e3d_clut_write(ep, regno, value);
+
+       return 0;
+}
+
+/* XXX This is a bit of a hack.  I can't figure out exactly how the
+ * XXX two 8bpp areas of the framebuffer work.  I imagine there is
+ * XXX a WID attribute somewhere else in the framebuffer which tells
+ * XXX the ramdac which of the two 8bpp framebuffer regions to take
+ * XXX the pixel from.  So, for now, render into both regions to make
+ * XXX sure the pixel shows up.
+ */
+static void e3d_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+       struct e3d_info *ep = info->par;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ep->lock, flags);
+       cfb_imageblit(info, image);
+       info->screen_base += ep->fb8_buf_diff;
+       cfb_imageblit(info, image);
+       info->screen_base -= ep->fb8_buf_diff;
+       spin_unlock_irqrestore(&ep->lock, flags);
+}
+
+static void e3d_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
+{
+       struct e3d_info *ep = info->par;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ep->lock, flags);
+       cfb_fillrect(info, rect);
+       info->screen_base += ep->fb8_buf_diff;
+       cfb_fillrect(info, rect);
+       info->screen_base -= ep->fb8_buf_diff;
+       spin_unlock_irqrestore(&ep->lock, flags);
+}
+
+static void e3d_copyarea(struct fb_info *info, const struct fb_copyarea *area)
+{
+       struct e3d_info *ep = info->par;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ep->lock, flags);
+       cfb_copyarea(info, area);
+       info->screen_base += ep->fb8_buf_diff;
+       cfb_copyarea(info, area);
+       info->screen_base -= ep->fb8_buf_diff;
+       spin_unlock_irqrestore(&ep->lock, flags);
+}
+
+static struct fb_ops e3d_ops = {
+       .owner                  = THIS_MODULE,
+       .fb_setcolreg           = e3d_setcolreg,
+       .fb_fillrect            = e3d_fillrect,
+       .fb_copyarea            = e3d_copyarea,
+       .fb_imageblit           = e3d_imageblit,
+};
+
+static int __devinit e3d_set_fbinfo(struct e3d_info *ep)
+{
+       struct fb_info *info = ep->info;
+       struct fb_var_screeninfo *var = &info->var;
+
+       info->flags = FBINFO_DEFAULT;
+       info->fbops = &e3d_ops;
+       info->screen_base = ep->fb_base;
+       info->screen_size = ep->fb_size;
+
+       info->pseudo_palette = ep->pseudo_palette;
+
+       /* Fill fix common fields */
+       strlcpy(info->fix.id, "e3d", sizeof(info->fix.id));
+        info->fix.smem_start = ep->fb_base_phys;
+        info->fix.smem_len = ep->fb_size;
+        info->fix.type = FB_TYPE_PACKED_PIXELS;
+       if (ep->depth == 32 || ep->depth == 24)
+               info->fix.visual = FB_VISUAL_TRUECOLOR;
+       else
+               info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+
+       var->xres = ep->width;
+       var->yres = ep->height;
+       var->xres_virtual = var->xres;
+       var->yres_virtual = var->yres;
+       var->bits_per_pixel = ep->depth;
+
+       var->red.offset = 8;
+       var->red.length = 8;
+       var->green.offset = 16;
+       var->green.length = 8;
+       var->blue.offset = 24;
+       var->blue.length = 8;
+       var->transp.offset = 0;
+       var->transp.length = 0;
+
+       if (fb_alloc_cmap(&info->cmap, 256, 0)) {
+               printk(KERN_ERR "e3d: Cannot allocate color map.\n");
+               return -ENOMEM;
+       }
+
+        return 0;
+}
+
+static int __devinit e3d_pci_register(struct pci_dev *pdev,
+                                     const struct pci_device_id *ent)
+{
+       struct pcidev_cookie *pcp = pdev->sysdata;
+       struct fb_info *info;
+       struct e3d_info *ep;
+       unsigned int line_length;
+       int err;
+
+       err = pci_enable_device(pdev);
+       if (err < 0) {
+               printk(KERN_ERR "e3d: Cannot enable PCI device %s\n",
+                      pci_name(pdev));
+               goto err_out;
+       }
+
+       info = framebuffer_alloc(sizeof(struct e3d_info), &pdev->dev);
+       if (!info) {
+               printk(KERN_ERR "e3d: Cannot allocate fb_info\n");
+               err = -ENOMEM;
+               goto err_disable;
+       }
+
+       ep = info->par;
+       ep->info = info;
+       ep->pdev = pdev;
+       spin_lock_init(&ep->lock);
+       ep->of_node = pcp->prom_node;
+       if (!ep->of_node) {
+               printk(KERN_ERR "e3d: Cannot find OF node of %s\n",
+                      pci_name(pdev));
+               err = -ENODEV;
+               goto err_release_fb;
+       }
+
+       /* Read the PCI base register of the frame buffer, which we
+        * need in order to interpret the RAMDAC_VID_*FB* values in
+        * the ramdac correctly.
+        */
+       pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0,
+                             &ep->fb_base_reg);
+       ep->fb_base_reg &= PCI_BASE_ADDRESS_MEM_MASK;
+
+       ep->regs_base_phys = pci_resource_start (pdev, 1);
+       err = pci_request_region(pdev, 1, "e3d regs");
+       if (err < 0) {
+               printk("e3d: Cannot request region 1 for %s\n",
+                      pci_name(pdev));
+               goto err_release_fb;
+       }
+       ep->ramdac = ioremap(ep->regs_base_phys + 0x8000, 0x1000);
+       if (!ep->ramdac)
+               goto err_release_pci1;
+
+       ep->fb8_0_off = readl(ep->ramdac + RAMDAC_VID_8FB_0);
+       ep->fb8_0_off -= ep->fb_base_reg;
+
+       ep->fb8_1_off = readl(ep->ramdac + RAMDAC_VID_8FB_1);
+       ep->fb8_1_off -= ep->fb_base_reg;
+
+       ep->fb8_buf_diff = ep->fb8_1_off - ep->fb8_0_off;
+
+       ep->fb_base_phys = pci_resource_start (pdev, 0);
+       ep->fb_base_phys += ep->fb8_0_off;
+
+       err = pci_request_region(pdev, 0, "e3d framebuffer");
+       if (err < 0) {
+               printk("e3d: Cannot request region 0 for %s\n",
+                      pci_name(pdev));
+               goto err_unmap_ramdac;
+       }
+
+       err = e3d_get_props(ep);
+       if (err)
+               goto err_release_pci0;
+
+       line_length = (readl(ep->ramdac + RAMDAC_VID_CFG) >> 16) & 0xff;
+       line_length = 1 << line_length;
+
+       switch (ep->depth) {
+       case 8:
+               info->fix.line_length = line_length;
+               break;
+       case 16:
+               info->fix.line_length = line_length * 2;
+               break;
+       case 24:
+               info->fix.line_length = line_length * 3;
+               break;
+       case 32:
+               info->fix.line_length = line_length * 4;
+               break;
+       }
+       ep->fb_size = info->fix.line_length * ep->height;
+
+       ep->fb_base = ioremap(ep->fb_base_phys, ep->fb_size);
+       if (!ep->fb_base)
+               goto err_release_pci0;
+
+       err = e3d_set_fbinfo(ep);
+       if (err)
+               goto err_unmap_fb;
+
+       pci_set_drvdata(pdev, info);
+
+       printk("e3d: Found device at %s\n", pci_name(pdev));
+
+       err = register_framebuffer(info);
+       if (err < 0) {
+               printk(KERN_ERR "e3d: Could not register framebuffer %s\n",
+                      pci_name(pdev));
+               goto err_unmap_fb;
+       }
+
+       return 0;
+
+err_unmap_fb:
+       iounmap(ep->fb_base);
+
+err_release_pci0:
+       pci_release_region(pdev, 0);
+
+err_unmap_ramdac:
+       iounmap(ep->ramdac);
+
+err_release_pci1:
+       pci_release_region(pdev, 1);
+
+err_release_fb:
+        framebuffer_release(info);
+
+err_disable:
+       pci_disable_device(pdev);
+
+err_out:
+       return err;
+}
+
+static void __devexit e3d_pci_unregister(struct pci_dev *pdev)
+{
+       struct fb_info *info = pci_get_drvdata(pdev);
+       struct e3d_info *ep = info->par;
+
+       unregister_framebuffer(info);
+
+       iounmap(ep->ramdac);
+       iounmap(ep->fb_base);
+
+       pci_release_region(pdev, 0);
+       pci_release_region(pdev, 1);
+
+        framebuffer_release(info);
+
+       pci_disable_device(pdev);
+}
+
+static struct pci_device_id e3d_pci_table[] = {
+       {       PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x7a0),        },
+       {       PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x7a2),        },
+       {       .vendor = PCI_VENDOR_ID_3DLABS,
+               .device = PCI_ANY_ID,
+               .subvendor = PCI_VENDOR_ID_3DLABS,
+               .subdevice = 0x0108,
+       },
+       {       .vendor = PCI_VENDOR_ID_3DLABS,
+               .device = PCI_ANY_ID,
+               .subvendor = PCI_VENDOR_ID_3DLABS,
+               .subdevice = 0x0140,
+       },
+       {       .vendor = PCI_VENDOR_ID_3DLABS,
+               .device = PCI_ANY_ID,
+               .subvendor = PCI_VENDOR_ID_3DLABS,
+               .subdevice = 0x1024,
+       },
+       { 0, }
+};
+
+static struct pci_driver e3d_driver = {
+       .name           = "e3d",
+       .id_table       = e3d_pci_table,
+       .probe          = e3d_pci_register,
+       .remove         = __devexit_p(e3d_pci_unregister),
+};
+
+static int __init e3d_init(void)
+{
+       if (fb_get_options("e3d", NULL))
+               return -ENODEV;
+
+       return pci_register_driver(&e3d_driver);
+}
+
+static void __exit e3d_exit(void)
+{
+       pci_unregister_driver(&e3d_driver);
+}
+
+module_init(e3d_init);
+module_exit(e3d_exit);
+
+MODULE_DESCRIPTION("framebuffer driver for Sun XVR-500 graphics");
+MODULE_AUTHOR("David S. Miller <davem@xxxxxxxxxxxxx>");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL");
-- 
1.5.1.3

>From 3de0ac1bb7515e44c08c2a63ae1ef36c1b146052 Mon Sep 17 00:00:00 2001
From: David S. Miller <davem@xxxxxxxxxxxxxxxxxxxx>
Date: Mon, 7 May 2007 17:15:27 -0700
Subject: [PATCH] [VIDEO]: Add Sun XVR-2500 framebuffer driver.

Signed-off-by: David S. Miller <davem@xxxxxxxxxxxxx>
---
 drivers/video/Kconfig      |   13 ++
 drivers/video/Makefile     |    1 +
 drivers/video/sunxvr2500.c |  279 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 293 insertions(+), 0 deletions(-)
 create mode 100644 drivers/video/sunxvr2500.c

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index c5dfe17..bb718b4 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1422,6 +1422,19 @@ config FB_XVR500
          mostly initialized the card already.  It is treated as a
          completely dumb framebuffer device.
 
+config FB_XVR2500
+       bool "Sun XVR-2500 3DLABS Wildcat support"
+       depends on FB && PCI && SPARC64
+       select FB_CFB_FILLRECT
+       select FB_CFB_COPYAREA
+       select FB_CFB_IMAGEBLIT
+       help
+         This is the framebuffer device for the Sun XVR-2500 and similar
+         graphics cards based upon the 3DLABS Wildcat chipset.  The driver
+         only works on sparc64 systems where the system firwmare has
+         mostly initialized the card already.  It is treated as a
+         completely dumb framebuffer device.
+
 config FB_PCI
        bool "PCI framebuffers"
        depends on (FB = y) && PCI && SPARC
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index b2cdd35..d1c5b75 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_FB_ATARI)            += atafb.o
 obj-$(CONFIG_FB_MAC)              += macfb.o
 obj-$(CONFIG_FB_HGA)              += hgafb.o
 obj-$(CONFIG_FB_XVR500)           += sunxvr500.o
+obj-$(CONFIG_FB_XVR2500)          += sunxvr2500.o
 obj-$(CONFIG_FB_IGA)              += igafb.o
 obj-$(CONFIG_FB_APOLLO)           += dnfb.o
 obj-$(CONFIG_FB_Q40)              += q40fb.o
diff --git a/drivers/video/sunxvr2500.c b/drivers/video/sunxvr2500.c
new file mode 100644
index 0000000..4c49c32
--- /dev/null
+++ b/drivers/video/sunxvr2500.c
@@ -0,0 +1,279 @@
+/* s3d.c: Sun 3DLABS XVR-2500 et al. driver for sparc64 systems
+ *
+ * Copyright (C) 2007 David S. Miller (davem@xxxxxxxxxxxxx)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/of_device.h>
+#include <asm/pbm.h>
+
+struct s3d_info {
+       struct fb_info          *info;
+       struct pci_dev          *pdev;
+
+       char __iomem            *fb_base;
+       unsigned long           fb_base_phys;
+
+       struct device_node      *of_node;
+
+       unsigned int            width;
+       unsigned int            height;
+       unsigned int            depth;
+       unsigned int            fb_size;
+
+       u32                     pseudo_palette[256];
+};
+
+static int __devinit s3d_get_props(struct s3d_info *sp)
+{
+       sp->width = of_getintprop_default(sp->of_node, "width", 0);
+       sp->height = of_getintprop_default(sp->of_node, "height", 0);
+       sp->depth = of_getintprop_default(sp->of_node, "depth", 8);
+
+       if (!sp->width || !sp->height) {
+               printk(KERN_ERR "s3d: Critical properties missing for %s\n",
+                      pci_name(sp->pdev));
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int s3d_setcolreg(unsigned regno,
+                        unsigned red, unsigned green, unsigned blue,
+                        unsigned transp, struct fb_info *info)
+{
+       u32 value;
+
+       if (regno >= 256)
+               return 1;
+
+       red >>= 8;
+       green >>= 8;
+       blue >>= 8;
+
+       value = (blue << 24) | (green << 16) | (red << 8);
+       ((u32 *)info->pseudo_palette)[regno] = value;
+
+       return 0;
+}
+
+static struct fb_ops s3d_ops = {
+       .owner                  = THIS_MODULE,
+       .fb_setcolreg           = s3d_setcolreg,
+       .fb_fillrect            = cfb_fillrect,
+       .fb_copyarea            = cfb_copyarea,
+       .fb_imageblit           = cfb_imageblit,
+};
+
+static int __devinit s3d_set_fbinfo(struct s3d_info *sp)
+{
+       struct fb_info *info = sp->info;
+       struct fb_var_screeninfo *var = &info->var;
+
+       info->flags = FBINFO_DEFAULT;
+       info->fbops = &s3d_ops;
+       info->screen_base = sp->fb_base;
+       info->screen_size = sp->fb_size;
+
+       info->pseudo_palette = sp->pseudo_palette;
+
+       /* Fill fix common fields */
+       strlcpy(info->fix.id, "s3d", sizeof(info->fix.id));
+        info->fix.smem_start = sp->fb_base_phys;
+        info->fix.smem_len = sp->fb_size;
+        info->fix.type = FB_TYPE_PACKED_PIXELS;
+       if (sp->depth == 32 || sp->depth == 24)
+               info->fix.visual = FB_VISUAL_TRUECOLOR;
+       else
+               info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+
+       var->xres = sp->width;
+       var->yres = sp->height;
+       var->xres_virtual = var->xres;
+       var->yres_virtual = var->yres;
+       var->bits_per_pixel = sp->depth;
+
+       var->red.offset = 8;
+       var->red.length = 8;
+       var->green.offset = 16;
+       var->green.length = 8;
+       var->blue.offset = 24;
+       var->blue.length = 8;
+       var->transp.offset = 0;
+       var->transp.length = 0;
+
+       if (fb_alloc_cmap(&info->cmap, 256, 0)) {
+               printk(KERN_ERR "s3d: Cannot allocate color map.\n");
+               return -ENOMEM;
+       }
+
+        return 0;
+}
+
+static int __devinit s3d_pci_register(struct pci_dev *pdev,
+                                     const struct pci_device_id *ent)
+{
+       struct pcidev_cookie *pcp = pdev->sysdata;
+       struct fb_info *info;
+       struct s3d_info *sp;
+       int err;
+
+       err = pci_enable_device(pdev);
+       if (err < 0) {
+               printk(KERN_ERR "s3d: Cannot enable PCI device %s\n",
+                      pci_name(pdev));
+               goto err_out;
+       }
+
+       info = framebuffer_alloc(sizeof(struct s3d_info), &pdev->dev);
+       if (!info) {
+               printk(KERN_ERR "s3d: Cannot allocate fb_info\n");
+               err = -ENOMEM;
+               goto err_disable;
+       }
+
+       sp = info->par;
+       sp->info = info;
+       sp->pdev = pdev;
+       sp->of_node = pcp->prom_node;
+       if (!sp->of_node) {
+               printk(KERN_ERR "s3d: Cannot find OF node of %s\n",
+                      pci_name(pdev));
+               err = -ENODEV;
+               goto err_release_fb;
+       }
+
+       sp->fb_base_phys = pci_resource_start (pdev, 1);
+
+       err = pci_request_region(pdev, 1, "s3d framebuffer");
+       if (err < 0) {
+               printk("s3d: Cannot request region 1 for %s\n",
+                      pci_name(pdev));
+               goto err_release_fb;
+       }
+
+       err = s3d_get_props(sp);
+       if (err)
+               goto err_release_pci;
+
+       /* XXX 'linebytes' is often wrong, it is equal to the width
+        * XXX with depth of 32 on my XVR-2500 which is clearly not
+        * XXX right.  So we don't try to use it.
+        */
+       switch (sp->depth) {
+       case 8:
+               info->fix.line_length = sp->width;
+               break;
+       case 16:
+               info->fix.line_length = sp->width * 2;
+               break;
+       case 24:
+               info->fix.line_length = sp->width * 3;
+               break;
+       case 32:
+               info->fix.line_length = sp->width * 4;
+               break;
+       }
+       sp->fb_size = info->fix.line_length * sp->height;
+
+       sp->fb_base = ioremap(sp->fb_base_phys, sp->fb_size);
+       if (!sp->fb_base)
+               goto err_release_pci;
+
+       err = s3d_set_fbinfo(sp);
+       if (err)
+               goto err_unmap_fb;
+
+       pci_set_drvdata(pdev, info);
+
+       printk("s3d: Found device at %s\n", pci_name(pdev));
+
+       err = register_framebuffer(info);
+       if (err < 0) {
+               printk(KERN_ERR "s3d: Could not register framebuffer %s\n",
+                      pci_name(pdev));
+               goto err_unmap_fb;
+       }
+
+       return 0;
+
+err_unmap_fb:
+       iounmap(sp->fb_base);
+
+err_release_pci:
+       pci_release_region(pdev, 1);
+
+err_release_fb:
+        framebuffer_release(info);
+
+err_disable:
+       pci_disable_device(pdev);
+
+err_out:
+       return err;
+}
+
+static void __devexit s3d_pci_unregister(struct pci_dev *pdev)
+{
+       struct fb_info *info = pci_get_drvdata(pdev);
+       struct s3d_info *sp = info->par;
+
+       unregister_framebuffer(info);
+
+       iounmap(sp->fb_base);
+
+       pci_release_region(pdev, 1);
+
+        framebuffer_release(info);
+
+       pci_disable_device(pdev);
+}
+
+static struct pci_device_id s3d_pci_table[] = {
+       {       PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x002c),       },
+       {       PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x002d),       },
+       {       PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x002e),       },
+       {       PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x002f),       },
+       {       PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x0030),       },
+       {       PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x0031),       },
+       {       PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x0032),       },
+       {       PCI_DEVICE(PCI_VENDOR_ID_3DLABS, 0x0033),       },
+       { 0, }
+};
+
+static struct pci_driver s3d_driver = {
+       .name           = "s3d",
+       .id_table       = s3d_pci_table,
+       .probe          = s3d_pci_register,
+       .remove         = __devexit_p(s3d_pci_unregister),
+};
+
+static int __init s3d_init(void)
+{
+       if (fb_get_options("s3d", NULL))
+               return -ENODEV;
+
+       return pci_register_driver(&s3d_driver);
+}
+
+static void __exit s3d_exit(void)
+{
+       pci_unregister_driver(&s3d_driver);
+}
+
+module_init(s3d_init);
+module_exit(s3d_exit);
+
+MODULE_DESCRIPTION("framebuffer driver for Sun XVR-2500 graphics");
+MODULE_AUTHOR("David S. Miller <davem@xxxxxxxxxxxxx>");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL");
-- 
1.5.1.3


--- End Message ---

Ruby Jobs
Java Jobs
Jobs in California
more...
what
job title, keywords
where
city, state, zip
jobs by job search
<Prev in Thread] Current Thread [Next in Thread>
Google Custom Search

Recently Viewed:
db.firebase.por...    text.xml.xalan....    qnx.openqnx.dev...    user-groups.zar...    internationaliz...    kde.devel.konve...    finance.e-gold....    emacs.latex.pre...    gis.therion/200...    web.webmin.gene...    yellowdog.gener...    vserver/2003-08...    redhat.release....    sysutils.tivoli...    xfree86.expert/...    mail.becky.user...    hardware.netapp...    netbsd.ports.xe...    python.distutil...    boot-loaders.gr...    culture.interne...    java.springfram...    activedir/2006-...   
Home | blog view | USPTO Patent Archive | advertise | OSDir is an inevitable website. super tiny logo

Free Magazines

Cisco News
Receive a free quarterly e-newsletter with exclusive articles on how Cisco IT uses its own products and solutions to enable the business.
subscribe

Systems Management News, the newspaper for IT systems administration and data center managers! Each issue of Systems Management News is chock-full of news and analysis to help you understand what's happening in your field.
subscribe

The Enterprise Newsweekly eWeek is the essential technology information source for builders of e-business.
subscribe

Oracle Magazine Oracle Magazine contains technology strategy articles, sample code, tips, Oracle and partner news, how to articles for developers and DBAs, and more. Oracle (NASDAQ: ORCL) is the world's largest enterprise software company.
subscribe

Total Telecom Total Telecom is "The Economist of the communications industry".
subscribe