Merge master.kernel.org:/pub/scm/linux/kernel/git/davej/cpufreq
[sfrench/cifs-2.6.git] / drivers / char / agp / intel-agp.c
index 61ac3809f997cbbb9932a8eb3f0428d63195a2d4..a3011de51f7c049438ae51fae97f3652522cf9a4 100644 (file)
@@ -2,14 +2,6 @@
  * Intel AGPGART routines.
  */
 
-/*
- * Intel(R) 855GM/852GM and 865G support added by David Dawes
- * <dawes@tungstengraphics.com>.
- *
- * Intel(R) 915G/915GM support added by Alan Hourihane
- * <alanh@tungstengraphics.com>.
- */
-
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/init.h>
@@ -17,6 +9,21 @@
 #include <linux/agp_backend.h>
 #include "agp.h"
 
+#define PCI_DEVICE_ID_INTEL_82946GZ_HB      0x2970
+#define PCI_DEVICE_ID_INTEL_82946GZ_IG      0x2972
+#define PCI_DEVICE_ID_INTEL_82965G_1_HB     0x2980
+#define PCI_DEVICE_ID_INTEL_82965G_1_IG     0x2982
+#define PCI_DEVICE_ID_INTEL_82965Q_HB       0x2990
+#define PCI_DEVICE_ID_INTEL_82965Q_IG       0x2992
+#define PCI_DEVICE_ID_INTEL_82965G_HB       0x29A0
+#define PCI_DEVICE_ID_INTEL_82965G_IG       0x29A2
+
+#define IS_I965 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82946GZ_HB || \
+                 agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965G_1_HB || \
+                 agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965Q_HB || \
+                 agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965G_HB)
+
+
 /* Intel 815 register */
 #define INTEL_815_APCONT       0x51
 #define INTEL_815_ATTBASE_MASK ~0x1FFFFFFF
@@ -40,6 +47,8 @@
 #define I915_GMCH_GMS_STOLEN_48M       (0x6 << 4)
 #define I915_GMCH_GMS_STOLEN_64M       (0x7 << 4)
 
+/* Intel 965G registers */
+#define I965_MSAC 0x62
 
 /* Intel 7505 registers */
 #define INTEL_I7505_APSIZE     0x74
@@ -160,7 +169,7 @@ static void *i8xx_alloc_pages(void)
 {
        struct page * page;
 
-       page = alloc_pages(GFP_KERNEL, 2);
+       page = alloc_pages(GFP_KERNEL | GFP_DMA32, 2);
        if (page == NULL)
                return NULL;
 
@@ -198,6 +207,9 @@ static int intel_i810_insert_entries(struct agp_memory *mem, off_t pg_start,
        int i, j, num_entries;
        void *temp;
 
+       if (mem->page_count == 0)
+               return 0;
+
        temp = agp_bridge->current_size;
        num_entries = A_SIZE_FIX(temp)->num_entries;
 
@@ -212,12 +224,16 @@ static int intel_i810_insert_entries(struct agp_memory *mem, off_t pg_start,
        if (type != 0 || mem->type != 0) {
                if ((type == AGP_DCACHE_MEMORY) && (mem->type == AGP_DCACHE_MEMORY)) {
                        /* special insert */
-                       global_cache_flush();
+                       if (!mem->is_flushed) {
+                               global_cache_flush();
+                               mem->is_flushed = TRUE;
+                       }
+
                        for (i = pg_start; i < (pg_start + mem->page_count); i++) {
                                writel((i*4096)|I810_PTE_LOCAL|I810_PTE_VALID, intel_i810_private.registers+I810_PTE_BASE+(i*4));
-                               readl(intel_i810_private.registers+I810_PTE_BASE+(i*4));        /* PCI Posting. */
                        }
-                       global_cache_flush();
+                       readl(intel_i810_private.registers+I810_PTE_BASE+((i-1)*4));    /* PCI Posting. */
+
                        agp_bridge->driver->tlb_flush(mem);
                        return 0;
                }
@@ -227,14 +243,17 @@ static int intel_i810_insert_entries(struct agp_memory *mem, off_t pg_start,
        }
 
 insert:
-       global_cache_flush();
+       if (!mem->is_flushed) {
+               global_cache_flush();
+               mem->is_flushed = TRUE;
+       }
+
        for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
                writel(agp_bridge->driver->mask_memory(agp_bridge,
                        mem->memory[i], mem->type),
                        intel_i810_private.registers+I810_PTE_BASE+(j*4));
-               readl(intel_i810_private.registers+I810_PTE_BASE+(j*4));        /* PCI Posting. */
        }
-       global_cache_flush();
+       readl(intel_i810_private.registers+I810_PTE_BASE+((j-1)*4));    /* PCI Posting. */
 
        agp_bridge->driver->tlb_flush(mem);
        return 0;
@@ -245,12 +264,14 @@ static int intel_i810_remove_entries(struct agp_memory *mem, off_t pg_start,
 {
        int i;
 
+       if (mem->page_count == 0)
+               return 0;
+
        for (i = pg_start; i < (mem->page_count + pg_start); i++) {
                writel(agp_bridge->scratch_page, intel_i810_private.registers+I810_PTE_BASE+(i*4));
-               readl(intel_i810_private.registers+I810_PTE_BASE+(i*4));        /* PCI Posting. */
        }
+       readl(intel_i810_private.registers+I810_PTE_BASE+((i-1)*4));
 
-       global_cache_flush();
        agp_bridge->driver->tlb_flush(mem);
        return 0;
 }
@@ -354,12 +375,18 @@ static struct aper_size_info_fixed intel_i830_sizes[] =
        /* The 64M mode still requires a 128k gatt */
        {64, 16384, 5},
        {256, 65536, 6},
+       {512, 131072, 7},
 };
 
 static struct _intel_i830_private {
        struct pci_dev *i830_dev;               /* device one */
        volatile u8 __iomem *registers;
        volatile u32 __iomem *gtt;              /* I915G */
+       /* gtt_entries is the number of gtt entries that are already mapped
+        * to stolen memory.  Stolen memory is larger than the memory mapped
+        * through gtt_entries, as it includes some reserved space for the BIOS
+        * popup and for the GTT.
+        */
        int gtt_entries;
 } intel_i830_private;
 
@@ -370,14 +397,41 @@ static void intel_i830_init_gtt_entries(void)
        u8 rdct;
        int local = 0;
        static const int ddt[4] = { 0, 16, 32, 64 };
-       int size;
+       int size; /* reserved space (in kb) at the top of stolen memory */
 
        pci_read_config_word(agp_bridge->dev,I830_GMCH_CTRL,&gmch_ctrl);
 
-       /* We obtain the size of the GTT, which is also stored (for some
-        * reason) at the top of stolen memory. Then we add 4KB to that
-        * for the video BIOS popup, which is also stored in there. */
-       size = agp_bridge->driver->fetch_size() + 4;
+       if (IS_I965) {
+               u32 pgetbl_ctl;
+
+               pci_read_config_dword(agp_bridge->dev, I810_PGETBL_CTL,
+                                     &pgetbl_ctl);
+               /* The 965 has a field telling us the size of the GTT,
+                * which may be larger than what is necessary to map the
+                * aperture.
+                */
+               switch (pgetbl_ctl & I965_PGETBL_SIZE_MASK) {
+               case I965_PGETBL_SIZE_128KB:
+                       size = 128;
+                       break;
+               case I965_PGETBL_SIZE_256KB:
+                       size = 256;
+                       break;
+               case I965_PGETBL_SIZE_512KB:
+                       size = 512;
+                       break;
+               default:
+                       printk(KERN_INFO PFX "Unknown page table size, "
+                              "assuming 512KB\n");
+                       size = 512;
+               }
+               size += 4; /* add in BIOS popup space */
+       } else {
+               /* On previous hardware, the GTT size was just what was
+                * required to map the aperture.
+                */
+               size = agp_bridge->driver->fetch_size() + 4;
+       }
 
        if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82830_HB ||
            agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82845G_HB) {
@@ -423,7 +477,7 @@ static void intel_i830_init_gtt_entries(void)
                        if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82915G_HB ||
                            agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82915GM_HB ||
                            agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945G_HB ||
-                           agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945GM_HB)
+                           agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945GM_HB || IS_I965 )
                                gtt_entries = MB(48) - KB(size);
                        else
                                gtt_entries = 0;
@@ -433,7 +487,7 @@ static void intel_i830_init_gtt_entries(void)
                        if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82915G_HB ||
                            agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82915GM_HB ||
                            agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945G_HB ||
-                           agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945GM_HB)
+                           agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945GM_HB || IS_I965)
                                gtt_entries = MB(64) - KB(size);
                        else
                                gtt_entries = 0;
@@ -566,6 +620,9 @@ static int intel_i830_insert_entries(struct agp_memory *mem,off_t pg_start, int
        int i,j,num_entries;
        void *temp;
 
+       if (mem->page_count == 0)
+               return 0;
+
        temp = agp_bridge->current_size;
        num_entries = A_SIZE_FIX(temp)->num_entries;
 
@@ -588,16 +645,18 @@ static int intel_i830_insert_entries(struct agp_memory *mem,off_t pg_start, int
                (mem->type != 0 && mem->type != AGP_PHYS_MEMORY))
                return -EINVAL;
 
-       global_cache_flush();   /* FIXME: Necessary ?*/
+       if (!mem->is_flushed) {
+               global_cache_flush();
+               mem->is_flushed = TRUE;
+       }
 
        for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
                writel(agp_bridge->driver->mask_memory(agp_bridge,
                        mem->memory[i], mem->type),
                        intel_i830_private.registers+I810_PTE_BASE+(j*4));
-               readl(intel_i830_private.registers+I810_PTE_BASE+(j*4));        /* PCI Posting. */
        }
+       readl(intel_i830_private.registers+I810_PTE_BASE+((j-1)*4));
 
-       global_cache_flush();
        agp_bridge->driver->tlb_flush(mem);
        return 0;
 }
@@ -607,7 +666,8 @@ static int intel_i830_remove_entries(struct agp_memory *mem,off_t pg_start,
 {
        int i;
 
-       global_cache_flush();
+       if (mem->page_count == 0)
+               return 0;
 
        if (pg_start < intel_i830_private.gtt_entries) {
                printk (KERN_INFO PFX "Trying to disable local/stolen memory\n");
@@ -616,10 +676,9 @@ static int intel_i830_remove_entries(struct agp_memory *mem,off_t pg_start,
 
        for (i = pg_start; i < (mem->page_count + pg_start); i++) {
                writel(agp_bridge->scratch_page, intel_i830_private.registers+I810_PTE_BASE+(i*4));
-               readl(intel_i830_private.registers+I810_PTE_BASE+(i*4));        /* PCI Posting. */
        }
+       readl(intel_i830_private.registers+I810_PTE_BASE+((i-1)*4));
 
-       global_cache_flush();
        agp_bridge->driver->tlb_flush(mem);
        return 0;
 }
@@ -676,6 +735,9 @@ static int intel_i915_insert_entries(struct agp_memory *mem,off_t pg_start,
        int i,j,num_entries;
        void *temp;
 
+       if (mem->page_count == 0)
+               return 0;
+
        temp = agp_bridge->current_size;
        num_entries = A_SIZE_FIX(temp)->num_entries;
 
@@ -698,15 +760,17 @@ static int intel_i915_insert_entries(struct agp_memory *mem,off_t pg_start,
                (mem->type != 0 && mem->type != AGP_PHYS_MEMORY))
                return -EINVAL;
 
-       global_cache_flush();
+       if (!mem->is_flushed) {
+               global_cache_flush();
+               mem->is_flushed = TRUE;
+       }
 
        for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
                writel(agp_bridge->driver->mask_memory(agp_bridge,
                        mem->memory[i], mem->type), intel_i830_private.gtt+j);
-               readl(intel_i830_private.gtt+j);        /* PCI Posting. */
        }
+       readl(intel_i830_private.gtt+j-1);
 
-       global_cache_flush();
        agp_bridge->driver->tlb_flush(mem);
        return 0;
 }
@@ -716,7 +780,8 @@ static int intel_i915_remove_entries(struct agp_memory *mem,off_t pg_start,
 {
        int i;
 
-       global_cache_flush();
+       if (mem->page_count == 0)
+               return 0;
 
        if (pg_start < intel_i830_private.gtt_entries) {
                printk (KERN_INFO PFX "Trying to disable local/stolen memory\n");
@@ -725,30 +790,34 @@ static int intel_i915_remove_entries(struct agp_memory *mem,off_t pg_start,
 
        for (i = pg_start; i < (mem->page_count + pg_start); i++) {
                writel(agp_bridge->scratch_page, intel_i830_private.gtt+i);
-               readl(intel_i830_private.gtt+i);
        }
+       readl(intel_i830_private.gtt+i-1);
 
-       global_cache_flush();
        agp_bridge->driver->tlb_flush(mem);
        return 0;
 }
 
-static int intel_i915_fetch_size(void)
+/* Return the aperture size by just checking the resource length.  The effect
+ * described in the spec of the MSAC registers is just changing of the
+ * resource size.
+ */
+static int intel_i9xx_fetch_size(void)
 {
-       struct aper_size_info_fixed *values;
-       u32 temp, offset;
+       int num_sizes = sizeof(intel_i830_sizes) / sizeof(*intel_i830_sizes);
+       int aper_size; /* size in megabytes */
+       int i;
 
-#define I915_256MB_ADDRESS_MASK (1<<27)
+       aper_size = pci_resource_len(intel_i830_private.i830_dev, 2) / MB(1);
 
-       values = A_SIZE_FIX(agp_bridge->driver->aperture_sizes);
+       for (i = 0; i < num_sizes; i++) {
+               if (aper_size == intel_i830_sizes[i].size) {
+                       agp_bridge->current_size = intel_i830_sizes + i;
+                       agp_bridge->previous_size = agp_bridge->current_size;
+                       return aper_size;
+               }
+       }
 
-       pci_read_config_dword(intel_i830_private.i830_dev, I915_GMADDR, &temp);
-       if (temp & I915_256MB_ADDRESS_MASK)
-               offset = 0;     /* 128MB aperture */
-       else
-               offset = 2;     /* 256MB aperture */
-       agp_bridge->previous_size = agp_bridge->current_size = (void *)(values + offset);
-       return values[offset].size;
+       return 0;
 }
 
 /* The intel i915 automatically initializes the agp aperture during POST.
@@ -792,6 +861,67 @@ static int intel_i915_create_gatt_table(struct agp_bridge_data *bridge)
        return 0;
 }
 
+/*
+ * The i965 supports 36-bit physical addresses, but to keep
+ * the format of the GTT the same, the bits that don't fit
+ * in a 32-bit word are shifted down to bits 4..7.
+ *
+ * Gcc is smart enough to notice that "(addr >> 28) & 0xf0"
+ * is always zero on 32-bit architectures, so no need to make
+ * this conditional.
+ */
+static unsigned long intel_i965_mask_memory(struct agp_bridge_data *bridge,
+       unsigned long addr, int type)
+{
+       /* Shift high bits down */
+       addr |= (addr >> 28) & 0xf0;
+
+       /* Type checking must be done elsewhere */
+       return addr | bridge->driver->masks[type].mask;
+}
+
+/* The intel i965 automatically initializes the agp aperture during POST.
+ * Use the memory already set aside for in the GTT.
+ */
+static int intel_i965_create_gatt_table(struct agp_bridge_data *bridge)
+{
+       int page_order;
+       struct aper_size_info_fixed *size;
+       int num_entries;
+       u32 temp;
+
+       size = agp_bridge->current_size;
+       page_order = size->page_order;
+       num_entries = size->num_entries;
+       agp_bridge->gatt_table_real = NULL;
+
+       pci_read_config_dword(intel_i830_private.i830_dev, I915_MMADDR, &temp);
+
+       temp &= 0xfff00000;
+       intel_i830_private.gtt = ioremap((temp + (512 * 1024)) , 512 * 1024);
+
+       if (!intel_i830_private.gtt)
+               return -ENOMEM;
+
+
+       intel_i830_private.registers = ioremap(temp,128 * 4096);
+       if (!intel_i830_private.registers)
+               return -ENOMEM;
+
+       temp = readl(intel_i830_private.registers+I810_PGETBL_CTL) & 0xfffff000;
+       global_cache_flush();   /* FIXME: ? */
+
+       /* we have to call this as early as possible after the MMIO base address is known */
+       intel_i830_init_gtt_entries();
+
+       agp_bridge->gatt_table = NULL;
+
+       agp_bridge->gatt_bus_addr = temp;
+
+       return 0;
+}
+
+
 static int intel_fetch_size(void)
 {
        int i;
@@ -1307,7 +1437,7 @@ static struct agp_bridge_driver intel_830_driver = {
        .owner                  = THIS_MODULE,
        .aperture_sizes         = intel_i830_sizes,
        .size_type              = FIXED_APER_SIZE,
-       .num_aperture_sizes     = 3,
+       .num_aperture_sizes     = 4,
        .needs_scratch_page     = TRUE,
        .configure              = intel_i830_configure,
        .fetch_size             = intel_i830_fetch_size,
@@ -1469,10 +1599,10 @@ static struct agp_bridge_driver intel_915_driver = {
        .owner                  = THIS_MODULE,
        .aperture_sizes         = intel_i830_sizes,
        .size_type              = FIXED_APER_SIZE,
-       .num_aperture_sizes     = 3,
+       .num_aperture_sizes     = 4,
        .needs_scratch_page     = TRUE,
        .configure              = intel_i915_configure,
-       .fetch_size             = intel_i915_fetch_size,
+       .fetch_size             = intel_i9xx_fetch_size,
        .cleanup                = intel_i915_cleanup,
        .tlb_flush              = intel_i810_tlbflush,
        .mask_memory            = intel_i810_mask_memory,
@@ -1489,6 +1619,29 @@ static struct agp_bridge_driver intel_915_driver = {
        .agp_destroy_page       = agp_generic_destroy_page,
 };
 
+static struct agp_bridge_driver intel_i965_driver = {
+       .owner                  = THIS_MODULE,
+       .aperture_sizes         = intel_i830_sizes,
+       .size_type              = FIXED_APER_SIZE,
+       .num_aperture_sizes     = 4,
+       .needs_scratch_page     = TRUE,
+       .configure              = intel_i915_configure,
+       .fetch_size             = intel_i9xx_fetch_size,
+       .cleanup                = intel_i915_cleanup,
+       .tlb_flush              = intel_i810_tlbflush,
+       .mask_memory            = intel_i965_mask_memory,
+       .masks                  = intel_i810_masks,
+       .agp_enable             = intel_i810_agp_enable,
+       .cache_flush            = global_cache_flush,
+       .create_gatt_table      = intel_i965_create_gatt_table,
+       .free_gatt_table        = intel_i830_free_gatt_table,
+       .insert_memory          = intel_i915_insert_entries,
+       .remove_memory          = intel_i915_remove_entries,
+       .alloc_by_type          = intel_i830_alloc_by_type,
+       .free_by_type           = intel_i810_free_by_type,
+       .agp_alloc_page         = agp_generic_alloc_page,
+       .agp_destroy_page       = agp_generic_destroy_page,
+};
 
 static struct agp_bridge_driver intel_7505_driver = {
        .owner                  = THIS_MODULE,
@@ -1684,6 +1837,35 @@ static int __devinit agp_intel_probe(struct pci_dev *pdev,
                        bridge->driver = &intel_845_driver;
                name = "945GM";
                break;
+       case PCI_DEVICE_ID_INTEL_82946GZ_HB:
+               if (find_i830(PCI_DEVICE_ID_INTEL_82946GZ_IG))
+                       bridge->driver = &intel_i965_driver;
+               else
+                       bridge->driver = &intel_845_driver;
+               name = "946GZ";
+               break;
+       case PCI_DEVICE_ID_INTEL_82965G_1_HB:
+               if (find_i830(PCI_DEVICE_ID_INTEL_82965G_1_IG))
+                       bridge->driver = &intel_i965_driver;
+               else
+                       bridge->driver = &intel_845_driver;
+               name = "965G";
+               break;
+       case PCI_DEVICE_ID_INTEL_82965Q_HB:
+               if (find_i830(PCI_DEVICE_ID_INTEL_82965Q_IG))
+                       bridge->driver = &intel_i965_driver;
+               else
+                       bridge->driver = &intel_845_driver;
+               name = "965Q";
+               break;
+       case PCI_DEVICE_ID_INTEL_82965G_HB:
+               if (find_i830(PCI_DEVICE_ID_INTEL_82965G_IG))
+                       bridge->driver = &intel_i965_driver;
+               else
+                       bridge->driver = &intel_845_driver;
+               name = "965G";
+               break;
+
        case PCI_DEVICE_ID_INTEL_7505_0:
                bridge->driver = &intel_7505_driver;
                name = "E7505";
@@ -1766,12 +1948,22 @@ static void __devexit agp_intel_remove(struct pci_dev *pdev)
        agp_put_bridge(bridge);
 }
 
+#ifdef CONFIG_PM
 static int agp_intel_resume(struct pci_dev *pdev)
 {
        struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
 
        pci_restore_state(pdev);
 
+       /* We should restore our graphics device's config space,
+        * as host bridge (00:00) resumes before graphics device (02:00),
+        * then our access to its pci space can work right.
+        */
+       if (intel_i810_private.i810_dev)
+               pci_restore_state(intel_i810_private.i810_dev);
+       if (intel_i830_private.i830_dev)
+               pci_restore_state(intel_i830_private.i830_dev);
+
        if (bridge->driver == &intel_generic_driver)
                intel_configure();
        else if (bridge->driver == &intel_850_driver)
@@ -1786,9 +1978,12 @@ static int agp_intel_resume(struct pci_dev *pdev)
                intel_i830_configure();
        else if (bridge->driver == &intel_810_driver)
                intel_i810_configure();
+       else if (bridge->driver == &intel_i965_driver)
+               intel_i915_configure();
 
        return 0;
 }
+#endif
 
 static struct pci_device_id agp_intel_pci_table[] = {
 #define ID(x)                                          \
@@ -1825,6 +2020,10 @@ static struct pci_device_id agp_intel_pci_table[] = {
        ID(PCI_DEVICE_ID_INTEL_82915GM_HB),
        ID(PCI_DEVICE_ID_INTEL_82945G_HB),
        ID(PCI_DEVICE_ID_INTEL_82945GM_HB),
+       ID(PCI_DEVICE_ID_INTEL_82946GZ_HB),
+       ID(PCI_DEVICE_ID_INTEL_82965G_1_HB),
+       ID(PCI_DEVICE_ID_INTEL_82965Q_HB),
+       ID(PCI_DEVICE_ID_INTEL_82965G_HB),
        { }
 };
 
@@ -1835,7 +2034,9 @@ static struct pci_driver agp_intel_pci_driver = {
        .id_table       = agp_intel_pci_table,
        .probe          = agp_intel_probe,
        .remove         = __devexit_p(agp_intel_remove),
+#ifdef CONFIG_PM
        .resume         = agp_intel_resume,
+#endif
 };
 
 static int __init agp_intel_init(void)