diff -urN linux-2.4.22.org/drivers/char/agp/agpgart_be.c linux-2.4.22/drivers/char/agp/agpgart_be.c --- linux-2.4.22.org/drivers/char/agp/agpgart_be.c 2003-11-21 18:32:42.000000000 +0100 +++ linux-2.4.22/drivers/char/agp/agpgart_be.c 2003-11-21 18:34:41.000000000 +0100 @@ -53,6 +53,10 @@ #include #endif +#ifdef CONFIG_AGP_UNINORTH +#include +#endif + #include #include "agp.h" @@ -81,7 +85,7 @@ { #if defined(__i386__) || defined(__x86_64__) asm volatile ("wbinvd":::"memory"); -#elif defined(__alpha__) || defined(__ia64__) || defined(__sparc__) +#elif defined(__alpha__) || defined(__ia64__) || defined(__sparc__) || defined(__powerpc__) /* ??? I wonder if we'll really need to flush caches, or if the core logic can manage to keep the system coherent. The ARM speaks only of using `cflush' to get things in memory in @@ -5918,6 +5922,405 @@ } #endif /* CONFIG_AGP_ATI */ +#ifdef CONFIG_AGP_UNINORTH + +static int uninorth_fetch_size(void) +{ + int i; + u32 temp; + aper_size_info_32 *values; + + pci_read_config_dword(agp_bridge.dev, UNI_N_CFG_GART_BASE, &temp); + temp &= ~(0xfffff000); + values = A_SIZE_32(agp_bridge.aperture_sizes); + + for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { + if (temp == values[i].size_value) { + agp_bridge.previous_size = + agp_bridge.current_size = (void *) (values + i); + agp_bridge.aperture_size_idx = i; + return values[i].size; + } + } + + agp_bridge.previous_size = + agp_bridge.current_size = (void *) (values + 1); + agp_bridge.aperture_size_idx = 1; + return values[1].size; + + return 0; +} + +static void uninorth_tlbflush(agp_memory * mem) +{ + pci_write_config_dword(agp_bridge.dev, UNI_N_CFG_GART_CTRL, + UNI_N_CFG_GART_ENABLE | UNI_N_CFG_GART_INVAL); + pci_write_config_dword(agp_bridge.dev, UNI_N_CFG_GART_CTRL, + UNI_N_CFG_GART_ENABLE); + pci_write_config_dword(agp_bridge.dev, UNI_N_CFG_GART_CTRL, + UNI_N_CFG_GART_ENABLE | UNI_N_CFG_GART_2xRESET); + pci_write_config_dword(agp_bridge.dev, UNI_N_CFG_GART_CTRL, + UNI_N_CFG_GART_ENABLE); +} + +static void uninorth_cleanup(void) +{ + pci_write_config_dword(agp_bridge.dev, UNI_N_CFG_GART_CTRL, + UNI_N_CFG_GART_ENABLE | UNI_N_CFG_GART_INVAL); + pci_write_config_dword(agp_bridge.dev, UNI_N_CFG_GART_CTRL, + 0); + pci_write_config_dword(agp_bridge.dev, UNI_N_CFG_GART_CTRL, + UNI_N_CFG_GART_2xRESET); + pci_write_config_dword(agp_bridge.dev, UNI_N_CFG_GART_CTRL, + 0); +} + +static int uninorth_configure(void) +{ + aper_size_info_32 *current_size; + + current_size = A_SIZE_32(agp_bridge.current_size); + + printk("agp: configuring for size idx: %d\n", current_size->size_value); + + /* aperture size and gatt addr */ + pci_write_config_dword(agp_bridge.dev, + UNI_N_CFG_GART_BASE, + (agp_bridge.gatt_bus_addr & 0xfffff000) + | current_size->size_value); + + /* HACK ALERT + * UniNorth seem to be buggy enough not to handle properly when + * the AGP aperture isn't mapped at bus physical address 0 + */ + agp_bridge.gart_bus_addr = 0; + pci_write_config_dword(agp_bridge.dev, + UNI_N_CFG_AGP_BASE, agp_bridge.gart_bus_addr); + + return 0; +} + +static unsigned long uninorth_mask_memory(unsigned long addr, int type) +{ + return addr;/* | agp_bridge.masks[0].mask;*/ +} + +static int uninorth_insert_memory(agp_memory * mem, + off_t pg_start, int type) +{ + int i, j, num_entries; + void *temp; + + temp = agp_bridge.current_size; + num_entries = A_SIZE_32(temp)->num_entries; + + if (type != 0 || mem->type != 0) { + /* The generic routines know nothing of memory types */ + return -EINVAL; + } + if ((pg_start + mem->page_count) > num_entries) { + return -EINVAL; + } + j = pg_start; + + while (j < (pg_start + mem->page_count)) { + if (!PGE_EMPTY(agp_bridge.gatt_table[j])) { + return -EBUSY; + } + j++; + } + + if (mem->is_flushed == FALSE) { + CACHE_FLUSH(); + mem->is_flushed = TRUE; + } + for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { + agp_bridge.gatt_table[j] = cpu_to_le32((mem->memory[i] & 0xfffff000) | 0x00000001UL); + flush_dcache_range(__va(mem->memory[i]), __va(mem->memory[i])+0x1000); + } + (void)in_le32((volatile u32*)&agp_bridge.gatt_table[pg_start]); + mb(); + flush_dcache_range((unsigned long)&agp_bridge.gatt_table[pg_start], + (unsigned long)&agp_bridge.gatt_table[pg_start + mem->page_count]); + + agp_bridge.tlb_flush(mem); + return 0; +} + +static void uninorth_agp_enable(u32 mode) +{ + struct pci_dev *device = NULL; + u32 command, scratch, cap_id; + u8 cap_ptr; + + pci_read_config_dword(agp_bridge.dev, + agp_bridge.capndx + 4, + &command); + + /* + * PASS1: go throu all devices that claim to be + * AGP devices and collect their data. + */ + + while ((device = pci_find_class(PCI_CLASS_DISPLAY_VGA << 8, + device)) != NULL) { + pci_read_config_dword(device, 0x04, &scratch); + + if (!(scratch & 0x00100000)) + continue; + + pci_read_config_byte(device, 0x34, &cap_ptr); + + if (cap_ptr != 0x00) { + do { + pci_read_config_dword(device, + cap_ptr, &cap_id); + + if ((cap_id & 0xff) != 0x02) + cap_ptr = (cap_id >> 8) & 0xff; + } + while (((cap_id & 0xff) != 0x02) && (cap_ptr != 0x00)); + } + if (cap_ptr != 0x00) { + /* + * Ok, here we have a AGP device. Disable impossible + * settings, and adjust the readqueue to the minimum. + */ + + pci_read_config_dword(device, cap_ptr + 4, &scratch); + + /* adjust RQ depth */ + command = + ((command & ~0xff000000) | + min_t(u32, (mode & 0xff000000), + min_t(u32, (command & 0xff000000), + (scratch & 0xff000000)))); + + /* disable SBA if it's not supported */ + if (!((command & 0x00000200) && + (scratch & 0x00000200) && + (mode & 0x00000200))) + command &= ~0x00000200; + + /* disable FW if it's not supported */ + if (!((command & 0x00000010) && + (scratch & 0x00000010) && + (mode & 0x00000010))) + command &= ~0x00000010; + + if (!((command & 4) && + (scratch & 4) && + (mode & 4))) + command &= ~0x00000004; + + if (!((command & 2) && + (scratch & 2) && + (mode & 2))) + command &= ~0x00000002; + + if (!((command & 1) && + (scratch & 1) && + (mode & 1))) + command &= ~0x00000001; + } + } + /* + * PASS2: Figure out the 4X/2X/1X setting and enable the + * target (our motherboard chipset). + */ + + if (command & 4) { + command &= ~3; /* 4X */ + } + if (command & 2) { + command &= ~5; /* 2X */ + } + if (command & 1) { + command &= ~6; /* 1X */ + } + command |= 0x00000100; + + uninorth_tlbflush(NULL); + + do { + pci_write_config_dword(agp_bridge.dev, + agp_bridge.capndx + 8, + command); + pci_read_config_dword(agp_bridge.dev, + agp_bridge.capndx + 8, + &scratch); + } while((scratch & 0x100) == 0); + + /* + * PASS3: Go throu all AGP devices and update the + * command registers. + */ + + while ((device = pci_find_class(PCI_CLASS_DISPLAY_VGA << 8, + device)) != NULL) { + pci_read_config_dword(device, 0x04, &scratch); + + if (!(scratch & 0x00100000)) + continue; + + pci_read_config_byte(device, 0x34, &cap_ptr); + + if (cap_ptr != 0x00) { + do { + pci_read_config_dword(device, + cap_ptr, &cap_id); + + if ((cap_id & 0xff) != 0x02) + cap_ptr = (cap_id >> 8) & 0xff; + } + while (((cap_id & 0xff) != 0x02) && (cap_ptr != 0x00)); + } + if (cap_ptr != 0x00) + pci_write_config_dword(device, cap_ptr + 8, command); + } + + uninorth_tlbflush(NULL); +} + +static int uninorth_create_gatt_table(void) +{ + char *table; + char *table_end; + int size; + int page_order; + int num_entries; + int i; + void *temp; + struct page *page; + + /* The generic routines can't handle 2 level gatt's */ + if (agp_bridge.size_type == LVL2_APER_SIZE) { + return -EINVAL; + } + + table = NULL; + i = agp_bridge.aperture_size_idx; + temp = agp_bridge.current_size; + size = page_order = num_entries = 0; + + do { + size = A_SIZE_32(temp)->size; + page_order = A_SIZE_32(temp)->page_order; + num_entries = A_SIZE_32(temp)->num_entries; + + table = (char *) __get_free_pages(GFP_KERNEL, page_order); + + if (table == NULL) { + i++; + agp_bridge.current_size = A_IDX32(); + } else { + agp_bridge.aperture_size_idx = i; + } + } while ((table == NULL) && + (i < agp_bridge.num_aperture_sizes)); + + if (table == NULL) { + return -ENOMEM; + } + table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); + + for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) + SetPageReserved(page); + + agp_bridge.gatt_table_real = (unsigned long *) table; + agp_bridge.gatt_table = (unsigned long *)table; + agp_bridge.gatt_bus_addr = virt_to_phys(table); + + for (i = 0; i < num_entries; i++) { + agp_bridge.gatt_table[i] = + (unsigned long) agp_bridge.scratch_page; + } + + flush_dcache_range((unsigned long)table, (unsigned long)table_end); + + return 0; +} + +static int uninorth_free_gatt_table(void) +{ + int page_order; + char *table, *table_end; + void *temp; + struct page *page; + + temp = agp_bridge.current_size; + page_order = A_SIZE_32(temp)->page_order; + + /* Do not worry about freeing memory, because if this is + * called, then all agp memory is deallocated and removed + * from the table. + */ + + table = (char *) agp_bridge.gatt_table_real; + table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); + + for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) + ClearPageReserved(page); + + free_pages((unsigned long) agp_bridge.gatt_table_real, page_order); + + return 0; +} + +/* Setup function */ +static gatt_mask uninorth_masks[] = +{ + {0x00000000, 0} +}; + +static aper_size_info_32 uninorth_sizes[7] = +{ +#if 0 /* Not sure uninorth supports that high aperture sizes */ + {256, 65536, 6, 64}, + {128, 32768, 5, 32}, + {64, 16384, 4, 16}, +#endif + {32, 8192, 3, 8}, + {16, 4096, 2, 4}, + {8, 2048, 1, 2}, + {4, 1024, 0, 1} +}; + +static int __init uninorth_setup (struct pci_dev *pdev) +{ + agp_bridge.masks = uninorth_masks; + agp_bridge.num_of_masks = 1; + agp_bridge.aperture_sizes = (void *)uninorth_sizes; + agp_bridge.size_type = U32_APER_SIZE; + agp_bridge.num_aperture_sizes = 4; //7; + agp_bridge.dev_private_data = NULL; + agp_bridge.needs_scratch_page = FALSE; + agp_bridge.configure = uninorth_configure; + agp_bridge.fetch_size = uninorth_fetch_size; + agp_bridge.cleanup = uninorth_cleanup; + agp_bridge.tlb_flush = uninorth_tlbflush; + agp_bridge.mask_memory = uninorth_mask_memory; + agp_bridge.agp_enable = uninorth_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = uninorth_create_gatt_table; + agp_bridge.free_gatt_table = uninorth_free_gatt_table; + agp_bridge.insert_memory = uninorth_insert_memory; + agp_bridge.remove_memory = agp_generic_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + agp_bridge.agp_alloc_page = agp_generic_alloc_page; + agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; + agp_bridge.cant_use_aperture = 1; + + return 0; + + (void) pdev; /* unused */ +} + +#endif /* CONFIG_AGP_UNINORTH */ + /* per-chipset initialization data. * note -- all chipsets for a single vendor MUST be grouped together */ @@ -6462,6 +6865,27 @@ ati_generic_setup }, #endif /* CONFIG_AGP_ATI */ +#ifdef CONFIG_AGP_UNINORTH + { PCI_DEVICE_ID_APPLE_UNI_N_AGP, + PCI_VENDOR_ID_APPLE, + APPLE_UNINORTH, + "Apple", + "UniNorth", + uninorth_setup }, + { PCI_DEVICE_ID_APPLE_UNI_N_AGP_P, + PCI_VENDOR_ID_APPLE, + APPLE_UNINORTH, + "Apple", + "UniNorth/Pangea", + uninorth_setup }, + { PCI_DEVICE_ID_APPLE_UNI_N_AGP15, + PCI_VENDOR_ID_APPLE, + APPLE_UNINORTH, + "Apple", + "UniNorth 1.5", + uninorth_setup }, +#endif /* CONFIG_AGP_UNINORTH */ + { 0, }, /* dummy final entry, always present */ }; diff -urN linux-2.4.22.org/drivers/char/Config.in linux-2.4.22/drivers/char/Config.in --- linux-2.4.22.org/drivers/char/Config.in 2003-11-21 18:32:29.000000000 +0100 +++ linux-2.4.22/drivers/char/Config.in 2003-11-21 18:34:42.000000000 +0100 @@ -210,6 +210,9 @@ comment ' from the tpqic02-support package. It is available at' comment ' metalab.unc.edu or ftp://titus.cfw.com/pub/Linux/util/' fi + if [ "$CONFIG_ALL_PPC" = "y" ]; then + bool ' Apple UniNorth support' CONFIG_AGP_UNINORTH + fi fi tristate 'IPMI top-level message handler' CONFIG_IPMI_HANDLER diff -urN linux-2.4.22.org/include/linux/agp_backend.h linux-2.4.22/include/linux/agp_backend.h --- linux-2.4.22.org/include/linux/agp_backend.h 2003-11-21 18:31:25.000000000 +0100 +++ linux-2.4.22/include/linux/agp_backend.h 2003-11-21 18:35:20.000000000 +0100 @@ -95,6 +95,7 @@ NVIDIA_NFORCE3, NVIDIA_GENERIC, HP_ZX1, + APPLE_UNINORTH, ATI_RS100, ATI_RS200, ATI_RS250,