📄 pmac_pci.c
字号:
/* * Support for PCI bridges found on Power Macintoshes. * At present the "bandit" and "chaos" bridges are supported. * Fortunately you access configuration space in the same * way with either bridge. * * Copyright (C) 1997 Paul Mackerras (paulus@cs.anu.edu.au) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */#include <linux/kernel.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/string.h>#include <linux/init.h>#include <linux/bootmem.h>#include <asm/init.h>#include <asm/io.h>#include <asm/prom.h>#include <asm/pci-bridge.h>#include <asm/machdep.h>#include "pci.h"struct bridge_data **bridges, *bridge_list;static int max_bus;struct uninorth_data { struct device_node* node; volatile unsigned int* cfg_addr; volatile unsigned int* cfg_data; void* iobase; unsigned long iobase_phys;};static struct uninorth_data uninorth_bridges[3];static int uninorth_count;static int uninorth_default = -1;static void add_bridges(struct device_node *dev);/* * Magic constants for enabling cache coherency in the bandit/PSX bridge. */#define APPLE_VENDID 0x106b#define BANDIT_DEVID 1#define BANDIT_DEVID_2 8#define BANDIT_REVID 3#define BANDIT_DEVNUM 11#define BANDIT_MAGIC 0x50#define BANDIT_COHERENT 0x40/* Obsolete, should be replaced by pmac_pci_dev_io_base() (below) */__pmacvoid *pci_io_base(unsigned int bus){ struct bridge_data *bp; if (bus > max_bus || (bp = bridges[bus]) == 0) return 0; return bp->io_base;}__pmacint pci_device_loc(struct device_node *dev, unsigned char *bus_ptr, unsigned char *devfn_ptr){ unsigned int *reg; int len; reg = (unsigned int *) get_property(dev, "reg", &len); if (reg == 0 || len < 5 * sizeof(unsigned int)) { /* doesn't look like a PCI device */ *bus_ptr = 0xff; *devfn_ptr = 0xff; return -1; } *bus_ptr = reg[0] >> 16; *devfn_ptr = reg[0] >> 8; return 0;}/* This routines figures out on which root bridge a given PCI device * is attached. */__pmacintpmac_pci_dev_root_bridge(unsigned char bus, unsigned char dev_fn){ struct device_node *node, *bridge_node; int bridge = uninorth_default; if (uninorth_count == 0) return 0; if (bus == 0 && PCI_SLOT(dev_fn) < 11) return 0; /* We look for the OF device corresponding to this bus/devfn pair. If we * don't find it, we default to the external PCI */ bridge_node = NULL; node = find_pci_device_OFnode(bus, dev_fn & 0xf8); if (node) { /* note: we don't stop on the first occurence since we need to go * up to the root bridge */ do { if (node->type && !strcmp(node->type, "pci") && device_is_compatible(node, "uni-north")) bridge_node = node; node=node->parent; } while (node); } if (bridge_node) { int i; for (i=0;i<uninorth_count;i++) if (uninorth_bridges[i].node == bridge_node) { bridge = i; break; } } if (bridge == -1) { printk(KERN_WARNING "pmac_pci: no default bridge !\n"); return 0; } return bridge; }__pmacvoid *pmac_pci_dev_io_base(unsigned char bus, unsigned char devfn, int physical){ int bridge = -1; if (uninorth_count != 0) bridge = pmac_pci_dev_root_bridge(bus, devfn); if (bridge == -1) { struct bridge_data *bp; if (bus > max_bus || (bp = bridges[bus]) == 0) return 0; return physical ? (void *) bp->io_base_phys : bp->io_base; } return physical ? (void *) uninorth_bridges[bridge].iobase_phys : uninorth_bridges[bridge].iobase;}__pmacvoid *pmac_pci_dev_mem_base(unsigned char bus, unsigned char devfn){ return 0;}/* This function only works for bus 0, uni-N uses a different mecanism for * other busses (see below) */#define UNI_N_CFA0(devfn, off) \ ((1 << (unsigned long)PCI_SLOT(dev_fn)) \ | (((unsigned long)PCI_FUNC(dev_fn)) << 8) \ | (((unsigned long)(off)) & 0xFCUL))/* This one is for type 1 config accesses */#define UNI_N_CFA1(bus, devfn, off) \ ((((unsigned long)(bus)) << 16) \ |(((unsigned long)(devfn)) << 8) \ |(((unsigned long)(off)) & 0xFCUL) \ |1UL) __pmac staticunsigned intuni_north_access_data(unsigned char bus, unsigned char dev_fn, unsigned char offset){ int bridge; unsigned int caddr; bridge = pmac_pci_dev_root_bridge(bus, dev_fn); if (bus == 0) caddr = UNI_N_CFA0(dev_fn, offset); else caddr = UNI_N_CFA1(bus, dev_fn, offset); if (bridge == -1) { printk(KERN_WARNING "pmac_pci: no default bridge !\n"); return 0; } /* Uninorth will return garbage if we don't read back the value ! */ out_le32(uninorth_bridges[bridge].cfg_addr, caddr); (void)in_le32(uninorth_bridges[bridge].cfg_addr); /* Yes, offset is & 7, not & 3 ! */ return (unsigned int)(uninorth_bridges[bridge].cfg_data) + (offset & 0x07);}__pmacint uni_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char *val){ unsigned int addr; *val = 0xff; addr = uni_north_access_data(bus, dev_fn, offset); if (!addr) return PCIBIOS_DEVICE_NOT_FOUND; *val = in_8((volatile unsigned char*)addr); return PCIBIOS_SUCCESSFUL;}__pmacint uni_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned short *val){ unsigned int addr; *val = 0xffff; addr = uni_north_access_data(bus, dev_fn, offset); if (!addr) return PCIBIOS_DEVICE_NOT_FOUND; *val = in_le16((volatile unsigned short*)addr); return PCIBIOS_SUCCESSFUL;}__pmacint uni_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned int *val){ unsigned int addr; *val = 0xffff; addr = uni_north_access_data(bus, dev_fn, offset); if (!addr) return PCIBIOS_DEVICE_NOT_FOUND; *val = in_le32((volatile unsigned int*)addr); return PCIBIOS_SUCCESSFUL;}__pmacint uni_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char val){ unsigned int addr; addr = uni_north_access_data(bus, dev_fn, offset); if (!addr) return PCIBIOS_DEVICE_NOT_FOUND; out_8((volatile unsigned char *)addr, val); (void)in_8((volatile unsigned char *)addr); return PCIBIOS_SUCCESSFUL;}__pmacint uni_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned short val){ unsigned int addr; addr = uni_north_access_data(bus, dev_fn, offset); if (!addr) return PCIBIOS_DEVICE_NOT_FOUND; out_le16((volatile unsigned short *)addr, val); (void)in_le16((volatile unsigned short *)addr); return PCIBIOS_SUCCESSFUL;}__pmacint uni_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned int val){ unsigned int addr; addr = uni_north_access_data(bus, dev_fn, offset); if (!addr) return PCIBIOS_DEVICE_NOT_FOUND; out_le32((volatile unsigned int *)addr, val); (void)in_le32((volatile unsigned int *)addr); return PCIBIOS_SUCCESSFUL;}__pmacint pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char *val){ struct bridge_data *bp; *val = 0xff; if (bus > max_bus || (bp = bridges[bus]) == 0) return PCIBIOS_DEVICE_NOT_FOUND; if (bus == bp->bus_number) { if (dev_fn < (11 << 3)) return PCIBIOS_DEVICE_NOT_FOUND; out_le32(bp->cfg_addr, (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + (offset & ~3)); } else { /* Bus number once again taken into consideration. * Change applied from 2.1.24. This makes devices located * behind PCI-PCI bridges visible. * -Ranjit Deshpande, 01/20/99 */ out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1); } udelay(2); *val = in_8(bp->cfg_data + (offset & 3)); return PCIBIOS_SUCCESSFUL;}__pmacint pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned short *val){ struct bridge_data *bp; *val = 0xffff; if (bus > max_bus || (bp = bridges[bus]) == 0) return PCIBIOS_DEVICE_NOT_FOUND; if ((offset & 1) != 0) return PCIBIOS_BAD_REGISTER_NUMBER; if (bus == bp->bus_number) { if (dev_fn < (11 << 3)) return PCIBIOS_DEVICE_NOT_FOUND; out_le32(bp->cfg_addr, (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + (offset & ~3)); } else { /* See pci_read_config_byte */ out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1); } udelay(2); *val = in_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3))); return PCIBIOS_SUCCESSFUL;}__pmacint pmac_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned int *val){ struct bridge_data *bp; *val = 0xffffffff; if (bus > max_bus || (bp = bridges[bus]) == 0) return PCIBIOS_DEVICE_NOT_FOUND; if ((offset & 3) != 0) return PCIBIOS_BAD_REGISTER_NUMBER; if (bus == bp->bus_number) { if (dev_fn < (11 << 3)) return PCIBIOS_DEVICE_NOT_FOUND; out_le32(bp->cfg_addr, (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + offset); } else { /* See pci_read_config_byte */ out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + offset + 1); } udelay(2); *val = in_le32((volatile unsigned int *)bp->cfg_data); return PCIBIOS_SUCCESSFUL;}__pmacint pmac_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char val){ struct bridge_data *bp; if (bus > max_bus || (bp = bridges[bus]) == 0) return PCIBIOS_DEVICE_NOT_FOUND; if (bus == bp->bus_number) { if (dev_fn < (11 << 3)) return PCIBIOS_DEVICE_NOT_FOUND; out_le32(bp->cfg_addr, (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + (offset & ~3)); } else { /* See pci_read_config_byte */ out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1); } udelay(2); out_8(bp->cfg_data + (offset & 3), val); return PCIBIOS_SUCCESSFUL;}__pmacint pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned short val){ struct bridge_data *bp; if (bus > max_bus || (bp = bridges[bus]) == 0) return PCIBIOS_DEVICE_NOT_FOUND; if ((offset & 1) != 0) return PCIBIOS_BAD_REGISTER_NUMBER; if (bus == bp->bus_number) { if (dev_fn < (11 << 3)) return PCIBIOS_DEVICE_NOT_FOUND; out_le32(bp->cfg_addr,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -