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