📄 uninorth-agp.c
字号:
/* * UniNorth AGPGART routines. */#include <linux/module.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/pagemap.h>#include <linux/agp_backend.h>#include <linux/delay.h>#include <asm/uninorth.h>#include <asm/pci-bridge.h>#include <asm/prom.h>#include <asm/pmac_feature.h>#include "agp.h"/* * NOTES for uninorth3 (G5 AGP) supports : * * There maybe also possibility to have bigger cache line size for * agp (see pmac_pci.c and look for cache line). Need to be investigated * by someone. * * PAGE size are hardcoded but this may change, see asm/page.h. * * Jerome Glisse <j.glisse@gmail.com> */static int uninorth_rev;static int is_u3;static int uninorth_fetch_size(void){ int i; u32 temp; struct 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->driver->aperture_sizes); for (i = 0; i < agp_bridge->driver->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(struct agp_memory *mem){ u32 ctrl = UNI_N_CFG_GART_ENABLE; if (is_u3) ctrl |= U3_N_CFG_GART_PERFRD; pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, ctrl | UNI_N_CFG_GART_INVAL); pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, ctrl); if (uninorth_rev <= 0x30) { pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, ctrl | UNI_N_CFG_GART_2xRESET); pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, ctrl); }}static void uninorth_cleanup(void){ u32 tmp; pci_read_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, &tmp); if (!(tmp & UNI_N_CFG_GART_ENABLE)) return; tmp |= UNI_N_CFG_GART_INVAL; pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, tmp); pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, 0); if (uninorth_rev <= 0x30) { 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){ struct aper_size_info_32 *current_size; current_size = A_SIZE_32(agp_bridge->current_size); printk(KERN_INFO PFX "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;#ifdef CONFIG_PPC64 /* Assume U3 or later on PPC64 systems */ /* high 4 bits of GART physical address go in UNI_N_CFG_AGP_BASE */ pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_AGP_BASE, (agp_bridge->gatt_bus_addr >> 32) & 0xf);#else pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_AGP_BASE, agp_bridge->gart_bus_addr);#endif if (is_u3) { pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_DUMMY_PAGE, agp_bridge->scratch_page_real >> 12); } return 0;}static int uninorth_insert_memory(struct 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) /* We 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 (agp_bridge->gatt_table[j]) return -EBUSY; j++; } for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { agp_bridge->gatt_table[j] = cpu_to_le32((mem->memory[i] & 0xFFFFF000UL) | 0x1UL); flush_dcache_range((unsigned long)__va(mem->memory[i]), (unsigned long)__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]); uninorth_tlbflush(mem); return 0;}static int u3_insert_memory(struct agp_memory *mem, off_t pg_start, int type){ int i, num_entries; void *temp; u32 *gp; temp = agp_bridge->current_size; num_entries = A_SIZE_32(temp)->num_entries; if (type != 0 || mem->type != 0) /* We know nothing of memory types */ return -EINVAL; if ((pg_start + mem->page_count) > num_entries) return -EINVAL; gp = (u32 *) &agp_bridge->gatt_table[pg_start]; for (i = 0; i < mem->page_count; ++i) { if (gp[i]) { printk("u3_insert_memory: entry 0x%x occupied (%x)\n", i, gp[i]); return -EBUSY; } } for (i = 0; i < mem->page_count; i++) { gp[i] = (mem->memory[i] >> PAGE_SHIFT) | 0x80000000UL; flush_dcache_range((unsigned long)__va(mem->memory[i]), (unsigned long)__va(mem->memory[i])+0x1000); } mb(); flush_dcache_range((unsigned long)gp, (unsigned long) &gp[i]); uninorth_tlbflush(mem); return 0;}int u3_remove_memory(struct agp_memory *mem, off_t pg_start, int type){ size_t i; u32 *gp; if (type != 0 || mem->type != 0) /* We know nothing of memory types */ return -EINVAL; gp = (u32 *) &agp_bridge->gatt_table[pg_start]; for (i = 0; i < mem->page_count; ++i) gp[i] = 0; mb(); flush_dcache_range((unsigned long)gp, (unsigned long) &gp[i]); uninorth_tlbflush(mem); return 0;}static void uninorth_agp_enable(struct agp_bridge_data *bridge, u32 mode){ u32 command, scratch, status; int timeout; pci_read_config_dword(bridge->dev, bridge->capndx + PCI_AGP_STATUS, &status); command = agp_collect_device_status(bridge, mode, status); command |= PCI_AGP_COMMAND_AGP; if (uninorth_rev == 0x21) { /* * Darwin disable AGP 4x on this revision, thus we * may assume it's broken. This is an AGP2 controller. */ command &= ~AGPSTAT2_4X; } if ((uninorth_rev >= 0x30) && (uninorth_rev <= 0x33)) { /* * We need to to set REQ_DEPTH to 7 for U3 versions 1.0, 2.1, * 2.2 and 2.3, Darwin do so. */ if ((command >> AGPSTAT_RQ_DEPTH_SHIFT) > 7) command = (command & ~AGPSTAT_RQ_DEPTH) | (7 << AGPSTAT_RQ_DEPTH_SHIFT); } uninorth_tlbflush(NULL); timeout = 0; do { pci_write_config_dword(bridge->dev, bridge->capndx + PCI_AGP_COMMAND, command); pci_read_config_dword(bridge->dev, bridge->capndx + PCI_AGP_COMMAND, &scratch); } while ((scratch & PCI_AGP_COMMAND_AGP) == 0 && ++timeout < 1000); if ((scratch & PCI_AGP_COMMAND_AGP) == 0) printk(KERN_ERR PFX "failed to write UniNorth AGP" " command register\n"); if (uninorth_rev >= 0x30) { /* This is an AGP V3 */ agp_device_command(command, (status & AGPSTAT_MODE_3_0)); } else { /* AGP V2 */ agp_device_command(command, 0); } uninorth_tlbflush(NULL);}#ifdef CONFIG_PM/* * These Power Management routines are _not_ called by the normal PCI PM layer, * but directly by the video driver through function pointers in the device * tree. */static int agp_uninorth_suspend(struct pci_dev *pdev){ struct agp_bridge_data *bridge; u32 cmd; u8 agp; struct pci_dev *device = NULL; bridge = agp_find_bridge(pdev); if (bridge == NULL) return -ENODEV; /* Only one suspend supported */ if (bridge->dev_private_data) return 0; /* turn off AGP on the video chip, if it was enabled */ for_each_pci_dev(device) { /* Don't touch the bridge yet, device first */ if (device == pdev) continue; /* Only deal with devices on the same bus here, no Mac has a P2P * bridge on the AGP port, and mucking around the entire PCI * tree is source of problems on some machines because of a bug * in some versions of pci_find_capability() when hitting a dead * device */ if (device->bus != pdev->bus) continue; agp = pci_find_capability(device, PCI_CAP_ID_AGP); if (!agp) continue; pci_read_config_dword(device, agp + PCI_AGP_COMMAND, &cmd); if (!(cmd & PCI_AGP_COMMAND_AGP)) continue; printk("uninorth-agp: disabling AGP on device %s\n", pci_name(device)); cmd &= ~PCI_AGP_COMMAND_AGP; pci_write_config_dword(device, agp + PCI_AGP_COMMAND, cmd); } /* turn off AGP on the bridge */ agp = pci_find_capability(pdev, PCI_CAP_ID_AGP); pci_read_config_dword(pdev, agp + PCI_AGP_COMMAND, &cmd); bridge->dev_private_data = (void *)cmd; if (cmd & PCI_AGP_COMMAND_AGP) { printk("uninorth-agp: disabling AGP on bridge %s\n", pci_name(pdev)); cmd &= ~PCI_AGP_COMMAND_AGP; pci_write_config_dword(pdev, agp + PCI_AGP_COMMAND, cmd); } /* turn off the GART */ uninorth_cleanup(); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -