diff options
author | Rob Herring <robh@kernel.org> | 2015-01-09 21:34:39 -0500 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2015-01-22 14:59:45 -0500 |
commit | 1f94a94f67e1083e19fb7b436dd7ca7a4ba03f2b (patch) | |
tree | ab2370828b760ed28ca7500c37318c78fcfc6fef /drivers/pci | |
parent | 453c02c28405978b7ef5d673023e5fec19bccf3f (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.c | 87 |
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); | |||
67 | EXPORT_SYMBOL(pci_bus_write_config_word); | 67 | EXPORT_SYMBOL(pci_bus_write_config_word); |
68 | EXPORT_SYMBOL(pci_bus_write_config_dword); | 68 | EXPORT_SYMBOL(pci_bus_write_config_dword); |
69 | 69 | ||
70 | int 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 | } | ||
90 | EXPORT_SYMBOL_GPL(pci_generic_config_read); | ||
91 | |||
92 | int 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 | } | ||
110 | EXPORT_SYMBOL_GPL(pci_generic_config_write); | ||
111 | |||
112 | int 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 | } | ||
130 | EXPORT_SYMBOL_GPL(pci_generic_config_read32); | ||
131 | |||
132 | int 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 | } | ||
155 | EXPORT_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 |