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