linux/arch/x86/kernel/mmconf-fam10h_64.c
<<
>>
Prefs
   1/*
   2 * AMD Family 10h mmconfig enablement
   3 */
   4
   5#include <linux/types.h>
   6#include <linux/mm.h>
   7#include <linux/string.h>
   8#include <linux/pci.h>
   9#include <linux/dmi.h>
  10#include <asm/pci-direct.h>
  11#include <linux/sort.h>
  12#include <asm/io.h>
  13#include <asm/msr.h>
  14#include <asm/acpi.h>
  15#include <asm/mmconfig.h>
  16#include <asm/pci_x86.h>
  17
  18struct pci_hostbridge_probe {
  19        u32 bus;
  20        u32 slot;
  21        u32 vendor;
  22        u32 device;
  23};
  24
  25static u64 __cpuinitdata fam10h_pci_mmconf_base;
  26static int __cpuinitdata fam10h_pci_mmconf_base_status;
  27
  28static struct pci_hostbridge_probe pci_probes[] __cpuinitdata = {
  29        { 0, 0x18, PCI_VENDOR_ID_AMD, 0x1200 },
  30        { 0xff, 0, PCI_VENDOR_ID_AMD, 0x1200 },
  31};
  32
  33struct range {
  34        u64 start;
  35        u64 end;
  36};
  37
  38static int __cpuinit cmp_range(const void *x1, const void *x2)
  39{
  40        const struct range *r1 = x1;
  41        const struct range *r2 = x2;
  42        int start1, start2;
  43
  44        start1 = r1->start >> 32;
  45        start2 = r2->start >> 32;
  46
  47        return start1 - start2;
  48}
  49
  50/*[47:0] */
  51/* need to avoid (0xfd<<32) and (0xfe<<32), ht used space */
  52#define FAM10H_PCI_MMCONF_BASE (0xfcULL<<32)
  53#define BASE_VALID(b) ((b != (0xfdULL << 32)) && (b != (0xfeULL << 32)))
  54static void __cpuinit get_fam10h_pci_mmconf_base(void)
  55{
  56        int i;
  57        unsigned bus;
  58        unsigned slot;
  59        int found;
  60
  61        u64 val;
  62        u32 address;
  63        u64 tom2;
  64        u64 base = FAM10H_PCI_MMCONF_BASE;
  65
  66        int hi_mmio_num;
  67        struct range range[8];
  68
  69        /* only try to get setting from BSP */
  70        /* -1 or 1 */
  71        if (fam10h_pci_mmconf_base_status)
  72                return;
  73
  74        if (!early_pci_allowed())
  75                goto fail;
  76
  77        found = 0;
  78        for (i = 0; i < ARRAY_SIZE(pci_probes); i++) {
  79                u32 id;
  80                u16 device;
  81                u16 vendor;
  82
  83                bus = pci_probes[i].bus;
  84                slot = pci_probes[i].slot;
  85                id = read_pci_config(bus, slot, 0, PCI_VENDOR_ID);
  86
  87                vendor = id & 0xffff;
  88                device = (id>>16) & 0xffff;
  89                if (pci_probes[i].vendor == vendor &&
  90                    pci_probes[i].device == device) {
  91                        found = 1;
  92                        break;
  93                }
  94        }
  95
  96        if (!found)
  97                goto fail;
  98
  99        /* SYS_CFG */
 100        address = MSR_K8_SYSCFG;
 101        rdmsrl(address, val);
 102
 103        /* TOP_MEM2 is not enabled? */
 104        if (!(val & (1<<21))) {
 105                tom2 = 0;
 106        } else {
 107                /* TOP_MEM2 */
 108                address = MSR_K8_TOP_MEM2;
 109                rdmsrl(address, val);
 110                tom2 = val & (0xffffULL<<32);
 111        }
 112
 113        if (base <= tom2)
 114                base = tom2 + (1ULL<<32);
 115
 116        /*
 117         * need to check if the range is in the high mmio range that is
 118         * above 4G
 119         */
 120        hi_mmio_num = 0;
 121        for (i = 0; i < 8; i++) {
 122                u32 reg;
 123                u64 start;
 124                u64 end;
 125                reg = read_pci_config(bus, slot, 1, 0x80 + (i << 3));
 126                if (!(reg & 3))
 127                        continue;
 128
 129                start = (((u64)reg) << 8) & (0xffULL << 32); /* 39:16 on 31:8*/
 130                reg = read_pci_config(bus, slot, 1, 0x84 + (i << 3));
 131                end = (((u64)reg) << 8) & (0xffULL << 32); /* 39:16 on 31:8*/
 132
 133                if (!end)
 134                        continue;
 135
 136                range[hi_mmio_num].start = start;
 137                range[hi_mmio_num].end = end;
 138                hi_mmio_num++;
 139        }
 140
 141        if (!hi_mmio_num)
 142                goto out;
 143
 144        /* sort the range */
 145        sort(range, hi_mmio_num, sizeof(struct range), cmp_range, NULL);
 146
 147        if (range[hi_mmio_num - 1].end < base)
 148                goto out;
 149        if (range[0].start > base)
 150                goto out;
 151
 152        /* need to find one window */
 153        base = range[0].start - (1ULL << 32);
 154        if ((base > tom2) && BASE_VALID(base))
 155                goto out;
 156        base = range[hi_mmio_num - 1].end + (1ULL << 32);
 157        if ((base > tom2) && BASE_VALID(base))
 158                goto out;
 159        /* need to find window between ranges */
 160        if (hi_mmio_num > 1)
 161        for (i = 0; i < hi_mmio_num - 1; i++) {
 162                if (range[i + 1].start > (range[i].end + (1ULL << 32))) {
 163                        base = range[i].end + (1ULL << 32);
 164                        if ((base > tom2) && BASE_VALID(base))
 165                                goto out;
 166                }
 167        }
 168
 169fail:
 170        fam10h_pci_mmconf_base_status = -1;
 171        return;
 172out:
 173        fam10h_pci_mmconf_base = base;
 174        fam10h_pci_mmconf_base_status = 1;
 175}
 176
 177void __cpuinit fam10h_check_enable_mmcfg(void)
 178{
 179        u64 val;
 180        u32 address;
 181
 182        if (!(pci_probe & PCI_CHECK_ENABLE_AMD_MMCONF))
 183                return;
 184
 185        address = MSR_FAM10H_MMIO_CONF_BASE;
 186        rdmsrl(address, val);
 187
 188        /* try to make sure that AP's setting is identical to BSP setting */
 189        if (val & FAM10H_MMIO_CONF_ENABLE) {
 190                unsigned busnbits;
 191                busnbits = (val >> FAM10H_MMIO_CONF_BUSRANGE_SHIFT) &
 192                        FAM10H_MMIO_CONF_BUSRANGE_MASK;
 193
 194                /* only trust the one handle 256 buses, if acpi=off */
 195                if (!acpi_pci_disabled || busnbits >= 8) {
 196                        u64 base;
 197                        base = val & (0xffffULL << 32);
 198                        if (fam10h_pci_mmconf_base_status <= 0) {
 199                                fam10h_pci_mmconf_base = base;
 200                                fam10h_pci_mmconf_base_status = 1;
 201                                return;
 202                        } else if (fam10h_pci_mmconf_base ==  base)
 203                                return;
 204                }
 205        }
 206
 207        /*
 208         * if it is not enabled, try to enable it and assume only one segment
 209         * with 256 buses
 210         */
 211        get_fam10h_pci_mmconf_base();
 212        if (fam10h_pci_mmconf_base_status <= 0)
 213                return;
 214
 215        printk(KERN_INFO "Enable MMCONFIG on AMD Family 10h\n");
 216        val &= ~((FAM10H_MMIO_CONF_BASE_MASK<<FAM10H_MMIO_CONF_BASE_SHIFT) |
 217             (FAM10H_MMIO_CONF_BUSRANGE_MASK<<FAM10H_MMIO_CONF_BUSRANGE_SHIFT));
 218        val |= fam10h_pci_mmconf_base | (8 << FAM10H_MMIO_CONF_BUSRANGE_SHIFT) |
 219               FAM10H_MMIO_CONF_ENABLE;
 220        wrmsrl(address, val);
 221}
 222
 223static int __devinit set_check_enable_amd_mmconf(const struct dmi_system_id *d)
 224{
 225        pci_probe |= PCI_CHECK_ENABLE_AMD_MMCONF;
 226        return 0;
 227}
 228
 229static const struct dmi_system_id __cpuinitconst mmconf_dmi_table[] = {
 230        {
 231                .callback = set_check_enable_amd_mmconf,
 232                .ident = "Sun Microsystems Machine",
 233                .matches = {
 234                        DMI_MATCH(DMI_SYS_VENDOR, "Sun Microsystems"),
 235                },
 236        },
 237        {}
 238};
 239
 240void __cpuinit check_enable_amd_mmconf_dmi(void)
 241{
 242        dmi_check_system(mmconf_dmi_table);
 243}
 244