amd_bus.c 13.4 KB
Newer Older
L
Linus Torvalds 已提交
1 2
#include <linux/init.h>
#include <linux/pci.h>
3
#include <linux/topology.h>
4
#include <linux/cpu.h>
5
#include <asm/pci_x86.h>
6 7

#ifdef CONFIG_X86_64
Y
Yinghai Lu 已提交
8
#include <asm/pci-direct.h>
L
Linus Torvalds 已提交
9 10
#include <asm/mpspec.h>
#include <linux/cpumask.h>
11
#endif
L
Linus Torvalds 已提交
12 13 14

/*
 * This discovers the pcibus <-> node mapping on AMD K8.
15
 * also get peer root bus resource for io,mmio
L
Linus Torvalds 已提交
16 17
 */

Y
Yinghai Lu 已提交
18 19 20 21
#ifdef CONFIG_NUMA

#define BUS_NR 256

22 23
#ifdef CONFIG_X86_64

Y
Yinghai Lu 已提交
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
static int mp_bus_to_node[BUS_NR];

void set_mp_bus_to_node(int busnum, int node)
{
	if (busnum >= 0 &&  busnum < BUS_NR)
		mp_bus_to_node[busnum] = node;
}

int get_mp_bus_to_node(int busnum)
{
	int node = -1;

	if (busnum < 0 || busnum > (BUS_NR - 1))
		return node;

	node = mp_bus_to_node[busnum];

	/*
	 * let numa_node_id to decide it later in dma_alloc_pages
	 * if there is no ram on that node
	 */
	if (node != -1 && !node_online(node))
		node = -1;

	return node;
}
50

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
#else /* CONFIG_X86_32 */

static unsigned char mp_bus_to_node[BUS_NR];

void set_mp_bus_to_node(int busnum, int node)
{
	if (busnum >= 0 &&  busnum < BUS_NR)
	mp_bus_to_node[busnum] = (unsigned char) node;
}

int get_mp_bus_to_node(int busnum)
{
	int node;

	if (busnum < 0 || busnum > (BUS_NR - 1))
		return 0;
	node = mp_bus_to_node[busnum];
	return node;
}

#endif /* CONFIG_X86_32 */

#endif /* CONFIG_NUMA */

#ifdef CONFIG_X86_64
Y
Yinghai Lu 已提交
76

77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
/*
 * sub bus (transparent) will use entres from 3 to store extra from root,
 * so need to make sure have enought slot there, increase PCI_BUS_NUM_RESOURCES?
 */
#define RES_NUM 16
struct pci_root_info {
	char name[12];
	unsigned int res_num;
	struct resource res[RES_NUM];
	int bus_min;
	int bus_max;
	int node;
	int link;
};

/* 4 at this time, it may become to 32 */
#define PCI_ROOT_NR 4
static int pci_root_num;
static struct pci_root_info pci_root_info[PCI_ROOT_NR];

97 98 99 100 101 102
void set_pci_bus_resources_arch_default(struct pci_bus *b)
{
	int i;
	int j;
	struct pci_root_info *info;

103 104
	/* if only one root bus, don't need to anything */
	if (pci_root_num < 2)
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
		return;

	for (i = 0; i < pci_root_num; i++) {
		if (pci_root_info[i].bus_min == b->number)
			break;
	}

	if (i == pci_root_num)
		return;

	info = &pci_root_info[i];
	for (j = 0; j < info->res_num; j++) {
		struct resource *res;
		struct resource *root;

		res = &info->res[j];
		b->resource[j] = res;
		if (res->flags & IORESOURCE_IO)
			root = &ioport_resource;
		else
			root = &iomem_resource;
		insert_resource(root, res);
	}
}

#define RANGE_NUM 16

struct res_range {
	size_t start;
	size_t end;
};

static void __init update_range(struct res_range *range, size_t start,
				size_t end)
{
	int i;
	int j;

	for (j = 0; j < RANGE_NUM; j++) {
		if (!range[j].end)
			continue;
146 147

		if (start <= range[j].start && end >= range[j].end) {
148 149
			range[j].start = 0;
			range[j].end = 0;
150 151 152 153 154 155 156 157 158 159
			continue;
		}

		if (start <= range[j].start && end < range[j].end && range[j].start < end + 1) {
			range[j].start = end + 1;
			continue;
		}


		if (start > range[j].start && end >= range[j].end && range[j].end > start - 1) {
160
			range[j].end = start - 1;
161 162 163 164
			continue;
		}

		if (start > range[j].start && end < range[j].end) {
165 166 167 168 169 170 171 172 173 174 175 176
			/* find the new spare */
			for (i = 0; i < RANGE_NUM; i++) {
				if (range[i].end == 0)
					break;
			}
			if (i < RANGE_NUM) {
				range[i].end = range[j].end;
				range[i].start = end + 1;
			} else {
				printk(KERN_ERR "run of slot in ranges\n");
			}
			range[j].end = start - 1;
177
			continue;
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
		}
	}
}

static void __init update_res(struct pci_root_info *info, size_t start,
			      size_t end, unsigned long flags, int merge)
{
	int i;
	struct resource *res;

	if (!merge)
		goto addit;

	/* try to merge it with old one */
	for (i = 0; i < info->res_num; i++) {
193 194 195
		size_t final_start, final_end;
		size_t common_start, common_end;

196 197 198
		res = &info->res[i];
		if (res->flags != flags)
			continue;
199 200 201 202 203 204 205 206 207 208 209 210

		common_start = max((size_t)res->start, start);
		common_end = min((size_t)res->end, end);
		if (common_start > common_end + 1)
			continue;

		final_start = min((size_t)res->start, start);
		final_end = max((size_t)res->end, end);

		res->start = final_start;
		res->end = final_end;
		return;
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
	}

addit:

	/* need to add that */
	if (info->res_num >= RES_NUM)
		return;

	res = &info->res[info->res_num];
	res->name = info->name;
	res->flags = flags;
	res->start = start;
	res->end = end;
	res->child = NULL;
	info->res_num++;
}

struct pci_hostbridge_probe {
	u32 bus;
	u32 slot;
	u32 vendor;
	u32 device;
};

static struct pci_hostbridge_probe pci_probes[] __initdata = {
	{ 0, 0x18, PCI_VENDOR_ID_AMD, 0x1100 },
	{ 0, 0x18, PCI_VENDOR_ID_AMD, 0x1200 },
	{ 0xff, 0, PCI_VENDOR_ID_AMD, 0x1200 },
	{ 0, 0x18, PCI_VENDOR_ID_AMD, 0x1300 },
};

242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
static u64 __initdata fam10h_mmconf_start;
static u64 __initdata fam10h_mmconf_end;
static void __init get_pci_mmcfg_amd_fam10h_range(void)
{
	u32 address;
	u64 base, msr;
	unsigned segn_busn_bits;

	/* assume all cpus from fam10h have mmconf */
        if (boot_cpu_data.x86 < 0x10)
		return;

	address = MSR_FAM10H_MMIO_CONF_BASE;
	rdmsrl(address, msr);

	/* mmconfig is not enable */
	if (!(msr & FAM10H_MMIO_CONF_ENABLE))
		return;

	base = msr & (FAM10H_MMIO_CONF_BASE_MASK<<FAM10H_MMIO_CONF_BASE_SHIFT);

	segn_busn_bits = (msr >> FAM10H_MMIO_CONF_BUSRANGE_SHIFT) &
			 FAM10H_MMIO_CONF_BUSRANGE_MASK;

	fam10h_mmconf_start = base;
	fam10h_mmconf_end = base + (1ULL<<(segn_busn_bits + 20)) - 1;
}

L
Linus Torvalds 已提交
270
/**
Y
Yinghai Lu 已提交
271 272
 * early_fill_mp_bus_to_node()
 * called before pcibios_scan_root and pci_scan_bus
L
Linus Torvalds 已提交
273 274 275
 * fills the mp_bus_to_cpumask array based according to the LDT Bus Number
 * Registers found in the K8 northbridge
 */
276
static int __init early_fill_mp_bus_info(void)
L
Linus Torvalds 已提交
277
{
278 279 280
	int i;
	int j;
	unsigned bus;
Y
Yinghai Lu 已提交
281
	unsigned slot;
282
	int found;
283
	int node;
284 285 286 287 288 289 290 291 292 293 294
	int link;
	int def_node;
	int def_link;
	struct pci_root_info *info;
	u32 reg;
	struct resource *res;
	size_t start;
	size_t end;
	struct res_range range[RANGE_NUM];
	u64 val;
	u32 address;
L
Linus Torvalds 已提交
295

296
#ifdef CONFIG_NUMA
Y
Yinghai Lu 已提交
297 298
	for (i = 0; i < BUS_NR; i++)
		mp_bus_to_node[i] = -1;
299
#endif
Y
Yinghai Lu 已提交
300 301 302 303

	if (!early_pci_allowed())
		return -1;

304 305 306 307 308
	found = 0;
	for (i = 0; i < ARRAY_SIZE(pci_probes); i++) {
		u32 id;
		u16 device;
		u16 vendor;
309

310 311 312
		bus = pci_probes[i].bus;
		slot = pci_probes[i].slot;
		id = read_pci_config(bus, slot, 0, PCI_VENDOR_ID);
313

314 315 316 317 318 319 320 321 322 323 324
		vendor = id & 0xffff;
		device = (id>>16) & 0xffff;
		if (pci_probes[i].vendor == vendor &&
		    pci_probes[i].device == device) {
			found = 1;
			break;
		}
	}

	if (!found)
		return 0;
325

326 327 328 329 330
	pci_root_num = 0;
	for (i = 0; i < 4; i++) {
		int min_bus;
		int max_bus;
		reg = read_pci_config(bus, slot, 1, 0xe0 + (i << 2));
331 332

		/* Check if that register is enabled for bus range */
333
		if ((reg & 7) != 3)
334 335
			continue;

336 337 338 339
		min_bus = (reg >> 16) & 0xff;
		max_bus = (reg >> 24) & 0xff;
		node = (reg >> 4) & 0x07;
#ifdef CONFIG_NUMA
340 341
		for (j = min_bus; j <= max_bus; j++)
			mp_bus_to_node[j] = (unsigned char) node;
342 343 344 345 346 347 348 349 350 351
#endif
		link = (reg >> 8) & 0x03;

		info = &pci_root_info[pci_root_num];
		info->bus_min = min_bus;
		info->bus_max = max_bus;
		info->node = node;
		info->link = link;
		sprintf(info->name, "PCI Bus #%02x", min_bus);
		pci_root_num++;
L
Linus Torvalds 已提交
352 353
	}

354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
	/* get the default node and link for left over res */
	reg = read_pci_config(bus, slot, 0, 0x60);
	def_node = (reg >> 8) & 0x07;
	reg = read_pci_config(bus, slot, 0, 0x64);
	def_link = (reg >> 8) & 0x03;

	memset(range, 0, sizeof(range));
	range[0].end = 0xffff;
	/* io port resource */
	for (i = 0; i < 4; i++) {
		reg = read_pci_config(bus, slot, 1, 0xc0 + (i << 3));
		if (!(reg & 3))
			continue;

		start = reg & 0xfff000;
		reg = read_pci_config(bus, slot, 1, 0xc4 + (i << 3));
		node = reg & 0x07;
		link = (reg >> 4) & 0x03;
		end = (reg & 0xfff000) | 0xfff;

		/* find the position */
		for (j = 0; j < pci_root_num; j++) {
			info = &pci_root_info[j];
			if (info->node == node && info->link == link)
				break;
		}
		if (j == pci_root_num)
			continue; /* not found */

		info = &pci_root_info[j];
384 385
		printk(KERN_DEBUG "node %d link %d: io port [%llx, %llx]\n",
		       node, link, (u64)start, (u64)end);
386 387 388 389 390

		/* kernel only handle 16 bit only */
		if (end > 0xffff)
			end = 0xffff;
		update_res(info, start, end, IORESOURCE_IO, 1);
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
		update_range(range, start, end);
	}
	/* add left over io port range to def node/link, [0, 0xffff] */
	/* find the position */
	for (j = 0; j < pci_root_num; j++) {
		info = &pci_root_info[j];
		if (info->node == def_node && info->link == def_link)
			break;
	}
	if (j < pci_root_num) {
		info = &pci_root_info[j];
		for (i = 0; i < RANGE_NUM; i++) {
			if (!range[i].end)
				continue;

			update_res(info, range[i].start, range[i].end,
				   IORESOURCE_IO, 1);
		}
	}

	memset(range, 0, sizeof(range));
	/* 0xfd00000000-0xffffffffff for HT */
413
	range[0].end = (0xfdULL<<32) - 1;
414 415 416 417

	/* need to take out [0, TOM) for RAM*/
	address = MSR_K8_TOP_MEM1;
	rdmsrl(address, val);
Y
Yinghai Lu 已提交
418
	end = (val & 0xffffff800000ULL);
419 420 421 422
	printk(KERN_INFO "TOM: %016lx aka %ldM\n", end, end>>20);
	if (end < (1ULL<<32))
		update_range(range, 0, end - 1);

423 424 425 426 427 428 429 430
	/* get mmconfig */
	get_pci_mmcfg_amd_fam10h_range();
	/* need to take out mmconf range */
	if (fam10h_mmconf_end) {
		printk(KERN_DEBUG "Fam 10h mmconf [%llx, %llx]\n", fam10h_mmconf_start, fam10h_mmconf_end);
		update_range(range, fam10h_mmconf_start, fam10h_mmconf_end);
	}

431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
	/* mmio resource */
	for (i = 0; i < 8; i++) {
		reg = read_pci_config(bus, slot, 1, 0x80 + (i << 3));
		if (!(reg & 3))
			continue;

		start = reg & 0xffffff00; /* 39:16 on 31:8*/
		start <<= 8;
		reg = read_pci_config(bus, slot, 1, 0x84 + (i << 3));
		node = reg & 0x07;
		link = (reg >> 4) & 0x03;
		end = (reg & 0xffffff00);
		end <<= 8;
		end |= 0xffff;

		/* find the position */
		for (j = 0; j < pci_root_num; j++) {
			info = &pci_root_info[j];
			if (info->node == node && info->link == link)
				break;
		}
		if (j == pci_root_num)
			continue; /* not found */

		info = &pci_root_info[j];
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497

		printk(KERN_DEBUG "node %d link %d: mmio [%llx, %llx]",
		       node, link, (u64)start, (u64)end);
		/*
		 * some sick allocation would have range overlap with fam10h
		 * mmconf range, so need to update start and end.
		 */
		if (fam10h_mmconf_end) {
			int changed = 0;
			u64 endx = 0;
			if (start >= fam10h_mmconf_start &&
			    start <= fam10h_mmconf_end) {
				start = fam10h_mmconf_end + 1;
				changed = 1;
			}

			if (end >= fam10h_mmconf_start &&
			    end <= fam10h_mmconf_end) {
				end = fam10h_mmconf_start - 1;
				changed = 1;
			}

			if (start < fam10h_mmconf_start &&
			    end > fam10h_mmconf_end) {
				/* we got a hole */
				endx = fam10h_mmconf_start - 1;
				update_res(info, start, endx, IORESOURCE_MEM, 0);
				update_range(range, start, endx);
				printk(KERN_CONT " ==> [%llx, %llx]", (u64)start, endx);
				start = fam10h_mmconf_end + 1;
				changed = 1;
			}
			if (changed) {
				if (start <= end) {
					printk(KERN_CONT " %s [%llx, %llx]", endx?"and":"==>", (u64)start, (u64)end);
				} else {
					printk(KERN_CONT "%s\n", endx?"":" ==> none");
					continue;
				}
			}
		}

498
		update_res(info, start, end, IORESOURCE_MEM, 1);
499
		update_range(range, start, end);
500
		printk(KERN_CONT "\n");
501 502 503 504 505 506 507 508 509 510 511
	}

	/* need to take out [4G, TOM2) for RAM*/
	/* SYS_CFG */
	address = MSR_K8_SYSCFG;
	rdmsrl(address, val);
	/* TOP_MEM2 is enabled? */
	if (val & (1<<21)) {
		/* TOP_MEM2 */
		address = MSR_K8_TOP_MEM2;
		rdmsrl(address, val);
Y
Yinghai Lu 已提交
512
		end = (val & 0xffffff800000ULL);
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
		printk(KERN_INFO "TOM2: %016lx aka %ldM\n", end, end>>20);
		update_range(range, 1ULL<<32, end - 1);
	}

	/*
	 * add left over mmio range to def node/link ?
	 * that is tricky, just record range in from start_min to 4G
	 */
	for (j = 0; j < pci_root_num; j++) {
		info = &pci_root_info[j];
		if (info->node == def_node && info->link == def_link)
			break;
	}
	if (j < pci_root_num) {
		info = &pci_root_info[j];

		for (i = 0; i < RANGE_NUM; i++) {
			if (!range[i].end)
				continue;

			update_res(info, range[i].start, range[i].end,
				   IORESOURCE_MEM, 1);
		}
	}

	for (i = 0; i < pci_root_num; i++) {
		int res_num;
		int busnum;

		info = &pci_root_info[i];
		res_num = info->res_num;
		busnum = info->bus_min;
		printk(KERN_DEBUG "bus: [%02x,%02x] on node %x link %x\n",
		       info->bus_min, info->bus_max, info->node, info->link);
		for (j = 0; j < res_num; j++) {
			res = &info->res[j];
			printk(KERN_DEBUG "bus: %02x index %x %s: [%llx, %llx]\n",
			       busnum, j,
			       (res->flags & IORESOURCE_IO)?"io port":"mmio",
			       res->start, res->end);
		}
	}

L
Linus Torvalds 已提交
556 557 558
	return 0;
}

559
#else  /* !CONFIG_X86_64 */
560

561 562 563
static int __init early_fill_mp_bus_info(void) { return 0; }

#endif /* !CONFIG_X86_64 */
564 565 566 567 568

/* common 32/64 bit code */

#define ENABLE_CF8_EXT_CFG      (1ULL << 46)

569
static void enable_pci_io_ecs(void *unused)
570 571 572 573 574 575 576 577 578
{
	u64 reg;
	rdmsrl(MSR_AMD64_NB_CFG, reg);
	if (!(reg & ENABLE_CF8_EXT_CFG)) {
		reg |= ENABLE_CF8_EXT_CFG;
		wrmsrl(MSR_AMD64_NB_CFG, reg);
	}
}

579 580
static int __cpuinit amd_cpu_notify(struct notifier_block *self,
				    unsigned long action, void *hcpu)
581
{
582
	int cpu = (long)hcpu;
583
	switch (action) {
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
	case CPU_ONLINE:
	case CPU_ONLINE_FROZEN:
		smp_call_function_single(cpu, enable_pci_io_ecs, NULL, 0);
		break;
	default:
		break;
	}
	return NOTIFY_OK;
}

static struct notifier_block __cpuinitdata amd_cpu_notifier = {
	.notifier_call	= amd_cpu_notify,
};

static int __init pci_io_ecs_init(void)
{
	int cpu;

602 603 604
	/* assume all cpus from fam10h have IO ECS */
        if (boot_cpu_data.x86 < 0x10)
		return 0;
605 606 607 608 609

	register_cpu_notifier(&amd_cpu_notifier);
	for_each_online_cpu(cpu)
		amd_cpu_notify(&amd_cpu_notifier, (unsigned long)CPU_ONLINE,
			       (void *)(long)cpu);
610
	pci_probe |= PCI_HAS_IO_ECS;
611

612 613 614
	return 0;
}

615 616 617 618 619 620
static int __init amd_postcore_init(void)
{
	if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
		return 0;

	early_fill_mp_bus_info();
621
	pci_io_ecs_init();
622 623 624 625 626

	return 0;
}

postcore_initcall(amd_postcore_init);