blob: 887d181b769b0cd2789f817f99efb64eaffc73b7 [file] [log] [blame]
Greg Kroah-Hartmanb2441312017-11-01 15:07:57 +01001// SPDX-License-Identifier: GPL-2.0
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * mmconfig.c - Low-level direct PCI config space access via MMCONFIG
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +03004 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 * This is an 64bit optimized version that always keeps the full mmconfig
6 * space mapped. This allows lockless config space operation.
7 */
8
9#include <linux/pci.h>
10#include <linux/init.h>
Greg Kroah-Hartman54549392005-06-23 17:35:56 -070011#include <linux/acpi.h>
Andi Kleend6ece542005-12-12 22:17:11 -080012#include <linux/bitmap.h>
Jiang Liu376f70a2012-06-22 14:55:12 +080013#include <linux/rcupdate.h>
Ingo Molnar66441bd2017-01-27 10:27:10 +010014#include <asm/e820/api.h>
Jaswinder Singh Rajput82487712008-12-27 18:32:28 +053015#include <asm/pci_x86.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016
Bjorn Helgaas8c577862009-11-13 17:34:59 -070017#define PREFIX "PCI: "
18
Al Viro8b8a4e32005-12-15 09:17:44 +000019static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn)
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -070020{
Bjorn Helgaasf6e1d8c2009-11-13 17:35:04 -070021 struct pci_mmcfg_region *cfg = pci_mmconfig_lookup(seg, bus);
Ivan Kokshayskya0ca9902008-01-14 17:31:09 -050022
Bjorn Helgaasf6e1d8c2009-11-13 17:35:04 -070023 if (cfg && cfg->virt)
24 return cfg->virt + (PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12));
25 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070026}
27
28static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
29 unsigned int devfn, int reg, int len, u32 *value)
30{
Al Viro8b8a4e32005-12-15 09:17:44 +000031 char __iomem *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -070032
Andi Kleen928cf8c2005-12-12 22:17:10 -080033 /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
Andi Kleenecc16ba2006-04-11 12:54:48 +020034 if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) {
Ivan Kokshayskya0ca9902008-01-14 17:31:09 -050035err: *value = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070036 return -EINVAL;
Andi Kleen49c93e82006-04-07 19:50:15 +020037 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070038
Jiang Liu376f70a2012-06-22 14:55:12 +080039 rcu_read_lock();
Andi Kleen928cf8c2005-12-12 22:17:10 -080040 addr = pci_dev_base(seg, bus, devfn);
Jiang Liu376f70a2012-06-22 14:55:12 +080041 if (!addr) {
42 rcu_read_unlock();
Ivan Kokshayskya0ca9902008-01-14 17:31:09 -050043 goto err;
Jiang Liu376f70a2012-06-22 14:55:12 +080044 }
Andi Kleen928cf8c2005-12-12 22:17:10 -080045
Linus Torvalds1da177e2005-04-16 15:20:36 -070046 switch (len) {
47 case 1:
dean gaudet3320ad92007-08-10 22:30:59 +020048 *value = mmio_config_readb(addr + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -070049 break;
50 case 2:
dean gaudet3320ad92007-08-10 22:30:59 +020051 *value = mmio_config_readw(addr + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -070052 break;
53 case 4:
dean gaudet3320ad92007-08-10 22:30:59 +020054 *value = mmio_config_readl(addr + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -070055 break;
56 }
Jiang Liu376f70a2012-06-22 14:55:12 +080057 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -070058
59 return 0;
60}
61
62static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
63 unsigned int devfn, int reg, int len, u32 value)
64{
Al Viro8b8a4e32005-12-15 09:17:44 +000065 char __iomem *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
Andi Kleen928cf8c2005-12-12 22:17:10 -080067 /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
Linus Torvalds1da177e2005-04-16 15:20:36 -070068 if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095)))
69 return -EINVAL;
70
Jiang Liu376f70a2012-06-22 14:55:12 +080071 rcu_read_lock();
Andi Kleen928cf8c2005-12-12 22:17:10 -080072 addr = pci_dev_base(seg, bus, devfn);
Jiang Liu376f70a2012-06-22 14:55:12 +080073 if (!addr) {
74 rcu_read_unlock();
Ivan Kokshayskya0ca9902008-01-14 17:31:09 -050075 return -EINVAL;
Jiang Liu376f70a2012-06-22 14:55:12 +080076 }
Andi Kleen928cf8c2005-12-12 22:17:10 -080077
Linus Torvalds1da177e2005-04-16 15:20:36 -070078 switch (len) {
79 case 1:
dean gaudet3320ad92007-08-10 22:30:59 +020080 mmio_config_writeb(addr + reg, value);
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 break;
82 case 2:
dean gaudet3320ad92007-08-10 22:30:59 +020083 mmio_config_writew(addr + reg, value);
Linus Torvalds1da177e2005-04-16 15:20:36 -070084 break;
85 case 4:
dean gaudet3320ad92007-08-10 22:30:59 +020086 mmio_config_writel(addr + reg, value);
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 break;
88 }
Jiang Liu376f70a2012-06-22 14:55:12 +080089 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -070090
91 return 0;
92}
93
Jiang Liuc0fa4072012-06-22 14:55:17 +080094const struct pci_raw_ops pci_mmcfg = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 .read = pci_mmcfg_read,
96 .write = pci_mmcfg_write,
97};
98
Greg Kroah-Hartmana18e3692012-12-21 14:02:53 -080099static void __iomem *mcfg_ioremap(struct pci_mmcfg_region *cfg)
OGAWA Hirofumi44de0202007-02-13 13:26:20 +0100100{
101 void __iomem *addr;
Yinghai Lu068258b2009-03-19 20:55:35 -0700102 u64 start, size;
Bjorn Helgaasdf5eb1d2009-11-13 17:34:08 -0700103 int num_buses;
OGAWA Hirofumi44de0202007-02-13 13:26:20 +0100104
Bjorn Helgaasd7e6b662009-11-13 17:34:18 -0700105 start = cfg->address + PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
106 num_buses = cfg->end_bus - cfg->start_bus + 1;
Bjorn Helgaasdf5eb1d2009-11-13 17:34:08 -0700107 size = PCI_MMCFG_BUS_OFFSET(num_buses);
Yinghai Lu068258b2009-03-19 20:55:35 -0700108 addr = ioremap_nocache(start, size);
Bjorn Helgaas8c577862009-11-13 17:34:59 -0700109 if (addr)
Bjorn Helgaasd7e6b662009-11-13 17:34:18 -0700110 addr -= PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
OGAWA Hirofumi44de0202007-02-13 13:26:20 +0100111 return addr;
112}
113
Olivier Galibertb7867392007-02-13 13:26:20 +0100114int __init pci_mmcfg_arch_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115{
Bjorn Helgaas3f0f5502009-11-13 17:34:39 -0700116 struct pci_mmcfg_region *cfg;
Olivier Galibertb7867392007-02-13 13:26:20 +0100117
Jiang Liu9cf01052012-06-22 14:55:13 +0800118 list_for_each_entry(cfg, &pci_mmcfg_list, list)
119 if (pci_mmcfg_arch_map(cfg)) {
Yinghai Lu0b64ad72008-02-15 01:28:41 -0800120 pci_mmcfg_arch_free();
Olivier Galibertb7867392007-02-13 13:26:20 +0100121 return 0;
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700122 }
Jiang Liu9cf01052012-06-22 14:55:13 +0800123
Matthew Wilcoxb6ce0682008-02-10 09:45:28 -0500124 raw_pci_ext_ops = &pci_mmcfg;
Jiang Liu9cf01052012-06-22 14:55:13 +0800125
Olivier Galibertb7867392007-02-13 13:26:20 +0100126 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127}
Yinghai Lu0b64ad72008-02-15 01:28:41 -0800128
129void __init pci_mmcfg_arch_free(void)
130{
Bjorn Helgaas3f0f5502009-11-13 17:34:39 -0700131 struct pci_mmcfg_region *cfg;
Yinghai Lu0b64ad72008-02-15 01:28:41 -0800132
Jiang Liu9cf01052012-06-22 14:55:13 +0800133 list_for_each_entry(cfg, &pci_mmcfg_list, list)
134 pci_mmcfg_arch_unmap(cfg);
135}
136
Greg Kroah-Hartmana18e3692012-12-21 14:02:53 -0800137int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg)
Jiang Liu9cf01052012-06-22 14:55:13 +0800138{
139 cfg->virt = mcfg_ioremap(cfg);
140 if (!cfg->virt) {
Jiang Liu24c97f02012-06-22 14:55:22 +0800141 pr_err(PREFIX "can't map MMCONFIG at %pR\n", &cfg->res);
Jiang Liu9cf01052012-06-22 14:55:13 +0800142 return -ENOMEM;
143 }
144
145 return 0;
146}
147
148void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg)
149{
150 if (cfg && cfg->virt) {
151 iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus));
152 cfg->virt = NULL;
Yinghai Lu0b64ad72008-02-15 01:28:41 -0800153 }
Yinghai Lu0b64ad72008-02-15 01:28:41 -0800154}