xref: /linux/arch/x86/pci/numachip.c (revision 95298d63c67673c654c08952672d016212b26054)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Numascale NumaConnect-specific PCI code
4  *
5  * Copyright (C) 2012 Numascale AS. All rights reserved.
6  *
7  * Send feedback to <support@numascale.com>
8  *
9  * PCI accessor functions derived from mmconfig_64.c
10  *
11  */
12 
13 #include <linux/pci.h>
14 #include <asm/pci_x86.h>
15 
16 static u8 limit __read_mostly;
17 
18 static inline char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn)
19 {
20 	struct pci_mmcfg_region *cfg = pci_mmconfig_lookup(seg, bus);
21 
22 	if (cfg && cfg->virt)
23 		return cfg->virt + (PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12));
24 	return NULL;
25 }
26 
27 static int pci_mmcfg_read_numachip(unsigned int seg, unsigned int bus,
28 			  unsigned int devfn, int reg, int len, u32 *value)
29 {
30 	char __iomem *addr;
31 
32 	/* Why do we have this when nobody checks it. How about a BUG()!? -AK */
33 	if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) {
34 err:		*value = -1;
35 		return -EINVAL;
36 	}
37 
38 	/* Ensure AMD Northbridges don't decode reads to other devices */
39 	if (unlikely(bus == 0 && devfn >= limit)) {
40 		*value = -1;
41 		return 0;
42 	}
43 
44 	rcu_read_lock();
45 	addr = pci_dev_base(seg, bus, devfn);
46 	if (!addr) {
47 		rcu_read_unlock();
48 		goto err;
49 	}
50 
51 	switch (len) {
52 	case 1:
53 		*value = mmio_config_readb(addr + reg);
54 		break;
55 	case 2:
56 		*value = mmio_config_readw(addr + reg);
57 		break;
58 	case 4:
59 		*value = mmio_config_readl(addr + reg);
60 		break;
61 	}
62 	rcu_read_unlock();
63 
64 	return 0;
65 }
66 
67 static int pci_mmcfg_write_numachip(unsigned int seg, unsigned int bus,
68 			   unsigned int devfn, int reg, int len, u32 value)
69 {
70 	char __iomem *addr;
71 
72 	/* Why do we have this when nobody checks it. How about a BUG()!? -AK */
73 	if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095)))
74 		return -EINVAL;
75 
76 	/* Ensure AMD Northbridges don't decode writes to other devices */
77 	if (unlikely(bus == 0 && devfn >= limit))
78 		return 0;
79 
80 	rcu_read_lock();
81 	addr = pci_dev_base(seg, bus, devfn);
82 	if (!addr) {
83 		rcu_read_unlock();
84 		return -EINVAL;
85 	}
86 
87 	switch (len) {
88 	case 1:
89 		mmio_config_writeb(addr + reg, value);
90 		break;
91 	case 2:
92 		mmio_config_writew(addr + reg, value);
93 		break;
94 	case 4:
95 		mmio_config_writel(addr + reg, value);
96 		break;
97 	}
98 	rcu_read_unlock();
99 
100 	return 0;
101 }
102 
103 static const struct pci_raw_ops pci_mmcfg_numachip = {
104 	.read = pci_mmcfg_read_numachip,
105 	.write = pci_mmcfg_write_numachip,
106 };
107 
108 int __init pci_numachip_init(void)
109 {
110 	int ret = 0;
111 	u32 val;
112 
113 	/* For remote I/O, restrict bus 0 access to the actual number of AMD
114 	   Northbridges, which starts at device number 0x18 */
115 	ret = raw_pci_read(0, 0, PCI_DEVFN(0x18, 0), 0x60, sizeof(val), &val);
116 	if (ret)
117 		goto out;
118 
119 	/* HyperTransport fabric size in bits 6:4 */
120 	limit = PCI_DEVFN(0x18 + ((val >> 4) & 7) + 1, 0);
121 
122 	/* Use NumaChip PCI accessors for non-extended and extended access */
123 	raw_pci_ops = raw_pci_ext_ops = &pci_mmcfg_numachip;
124 out:
125 	return ret;
126 }
127