aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorRob Herring <robh@kernel.org>2015-01-09 21:34:39 -0500
committerBjorn Helgaas <bhelgaas@google.com>2015-01-22 14:59:45 -0500
commit1f94a94f67e1083e19fb7b436dd7ca7a4ba03f2b (patch)
treeab2370828b760ed28ca7500c37318c78fcfc6fef /drivers/pci
parent453c02c28405978b7ef5d673023e5fec19bccf3f (diff)
PCI: Add generic config accessors
Many PCI controllers' configuration space accesses are memory-mapped and vary only in address calculation and access checks. There are 2 main access methods: a decoded address space such as ECAM or a single address and data register similar to x86. This implementation can support both cases as well as be used in cases that need additional pre- or post-access handling. Add a new pci_ops member, map_bus, which can do access checks and any necessary setup. It returns the address to use for the configuration space access. The access types supported are 32-bit only accesses or correct byte, word, or dword sized accesses. Tested-by: Thierry Reding <treding@nvidia.com> Signed-off-by: Rob Herring <robh@kernel.org> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/access.c87
1 files changed, 87 insertions, 0 deletions
diff --git a/drivers/pci/access.c b/drivers/pci/access.c
index 49dd766852ba..d9b64a175990 100644
--- a/drivers/pci/access.c
+++ b/drivers/pci/access.c
@@ -67,6 +67,93 @@ EXPORT_SYMBOL(pci_bus_write_config_byte);
67EXPORT_SYMBOL(pci_bus_write_config_word); 67EXPORT_SYMBOL(pci_bus_write_config_word);
68EXPORT_SYMBOL(pci_bus_write_config_dword); 68EXPORT_SYMBOL(pci_bus_write_config_dword);
69 69
70int pci_generic_config_read(struct pci_bus *bus, unsigned int devfn,
71 int where, int size, u32 *val)
72{
73 void __iomem *addr;
74
75 addr = bus->ops->map_bus(bus, devfn, where);
76 if (!addr) {
77 *val = ~0;
78 return PCIBIOS_DEVICE_NOT_FOUND;
79 }
80
81 if (size == 1)
82 *val = readb(addr);
83 else if (size == 2)
84 *val = readw(addr);
85 else
86 *val = readl(addr);
87
88 return PCIBIOS_SUCCESSFUL;
89}
90EXPORT_SYMBOL_GPL(pci_generic_config_read);
91
92int pci_generic_config_write(struct pci_bus *bus, unsigned int devfn,
93 int where, int size, u32 val)
94{
95 void __iomem *addr;
96
97 addr = bus->ops->map_bus(bus, devfn, where);
98 if (!addr)
99 return PCIBIOS_DEVICE_NOT_FOUND;
100
101 if (size == 1)
102 writeb(val, addr);
103 else if (size == 2)
104 writew(val, addr);
105 else
106 writel(val, addr);
107
108 return PCIBIOS_SUCCESSFUL;
109}
110EXPORT_SYMBOL_GPL(pci_generic_config_write);
111
112int pci_generic_config_read32(struct pci_bus *bus, unsigned int devfn,
113 int where, int size, u32 *val)
114{
115 void __iomem *addr;
116
117 addr = bus->ops->map_bus(bus, devfn, where & ~0x3);
118 if (!addr) {
119 *val = ~0;
120 return PCIBIOS_DEVICE_NOT_FOUND;
121 }
122
123 *val = readl(addr);
124
125 if (size <= 2)
126 *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1);
127
128 return PCIBIOS_SUCCESSFUL;
129}
130EXPORT_SYMBOL_GPL(pci_generic_config_read32);
131
132int pci_generic_config_write32(struct pci_bus *bus, unsigned int devfn,
133 int where, int size, u32 val)
134{
135 void __iomem *addr;
136 u32 mask, tmp;
137
138 addr = bus->ops->map_bus(bus, devfn, where & ~0x3);
139 if (!addr)
140 return PCIBIOS_DEVICE_NOT_FOUND;
141
142 if (size == 4) {
143 writel(val, addr);
144 return PCIBIOS_SUCCESSFUL;
145 } else {
146 mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8));
147 }
148
149 tmp = readl(addr) & mask;
150 tmp |= val << ((where & 0x3) * 8);
151 writel(tmp, addr);
152
153 return PCIBIOS_SUCCESSFUL;
154}
155EXPORT_SYMBOL_GPL(pci_generic_config_write32);
156
70/** 157/**
71 * pci_bus_set_ops - Set raw operations of pci bus 158 * pci_bus_set_ops - Set raw operations of pci bus
72 * @bus: pci bus struct 159 * @bus: pci bus struct