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