diff options
Diffstat (limited to 'arch/sparc64/kernel/pci_common.c')
-rw-r--r-- | arch/sparc64/kernel/pci_common.c | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/arch/sparc64/kernel/pci_common.c b/arch/sparc64/kernel/pci_common.c index 76faaa8135dd..f974fefc3ebc 100644 --- a/arch/sparc64/kernel/pci_common.c +++ b/arch/sparc64/kernel/pci_common.c | |||
@@ -14,6 +14,200 @@ | |||
14 | #include <asm/oplib.h> | 14 | #include <asm/oplib.h> |
15 | 15 | ||
16 | #include "pci_impl.h" | 16 | #include "pci_impl.h" |
17 | #include "pci_sun4v.h" | ||
18 | |||
19 | static int config_out_of_range(struct pci_pbm_info *pbm, | ||
20 | unsigned long bus, | ||
21 | unsigned long devfn, | ||
22 | unsigned long reg) | ||
23 | { | ||
24 | if (bus < pbm->pci_first_busno || | ||
25 | bus > pbm->pci_last_busno) | ||
26 | return 1; | ||
27 | return 0; | ||
28 | } | ||
29 | |||
30 | static void *sun4u_config_mkaddr(struct pci_pbm_info *pbm, | ||
31 | unsigned long bus, | ||
32 | unsigned long devfn, | ||
33 | unsigned long reg) | ||
34 | { | ||
35 | unsigned long rbits = pbm->config_space_reg_bits; | ||
36 | |||
37 | if (config_out_of_range(pbm, bus, devfn, reg)) | ||
38 | return NULL; | ||
39 | |||
40 | reg = (reg & ((1 << rbits) - 1)); | ||
41 | devfn <<= rbits; | ||
42 | bus <<= rbits + 8; | ||
43 | |||
44 | return (void *) (pbm->config_space | bus | devfn | reg); | ||
45 | } | ||
46 | |||
47 | static int sun4u_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, | ||
48 | int where, int size, u32 *value) | ||
49 | { | ||
50 | struct pci_pbm_info *pbm = bus_dev->sysdata; | ||
51 | unsigned char bus = bus_dev->number; | ||
52 | u32 *addr; | ||
53 | u16 tmp16; | ||
54 | u8 tmp8; | ||
55 | |||
56 | if (bus_dev == pbm->pci_bus && devfn == 0x00) | ||
57 | return pci_host_bridge_read_pci_cfg(bus_dev, devfn, where, | ||
58 | size, value); | ||
59 | |||
60 | switch (size) { | ||
61 | case 1: | ||
62 | *value = 0xff; | ||
63 | break; | ||
64 | case 2: | ||
65 | *value = 0xffff; | ||
66 | break; | ||
67 | case 4: | ||
68 | *value = 0xffffffff; | ||
69 | break; | ||
70 | } | ||
71 | |||
72 | addr = sun4u_config_mkaddr(pbm, bus, devfn, where); | ||
73 | if (!addr) | ||
74 | return PCIBIOS_SUCCESSFUL; | ||
75 | |||
76 | switch (size) { | ||
77 | case 1: | ||
78 | pci_config_read8((u8 *)addr, &tmp8); | ||
79 | *value = (u32) tmp8; | ||
80 | break; | ||
81 | |||
82 | case 2: | ||
83 | if (where & 0x01) { | ||
84 | printk("pci_read_config_word: misaligned reg [%x]\n", | ||
85 | where); | ||
86 | return PCIBIOS_SUCCESSFUL; | ||
87 | } | ||
88 | pci_config_read16((u16 *)addr, &tmp16); | ||
89 | *value = (u32) tmp16; | ||
90 | break; | ||
91 | |||
92 | case 4: | ||
93 | if (where & 0x03) { | ||
94 | printk("pci_read_config_dword: misaligned reg [%x]\n", | ||
95 | where); | ||
96 | return PCIBIOS_SUCCESSFUL; | ||
97 | } | ||
98 | pci_config_read32(addr, value); | ||
99 | break; | ||
100 | } | ||
101 | return PCIBIOS_SUCCESSFUL; | ||
102 | } | ||
103 | |||
104 | static int sun4u_write_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, | ||
105 | int where, int size, u32 value) | ||
106 | { | ||
107 | struct pci_pbm_info *pbm = bus_dev->sysdata; | ||
108 | unsigned char bus = bus_dev->number; | ||
109 | u32 *addr; | ||
110 | |||
111 | if (bus_dev == pbm->pci_bus && devfn == 0x00) | ||
112 | return pci_host_bridge_write_pci_cfg(bus_dev, devfn, where, | ||
113 | size, value); | ||
114 | addr = sun4u_config_mkaddr(pbm, bus, devfn, where); | ||
115 | if (!addr) | ||
116 | return PCIBIOS_SUCCESSFUL; | ||
117 | |||
118 | switch (size) { | ||
119 | case 1: | ||
120 | pci_config_write8((u8 *)addr, value); | ||
121 | break; | ||
122 | |||
123 | case 2: | ||
124 | if (where & 0x01) { | ||
125 | printk("pci_write_config_word: misaligned reg [%x]\n", | ||
126 | where); | ||
127 | return PCIBIOS_SUCCESSFUL; | ||
128 | } | ||
129 | pci_config_write16((u16 *)addr, value); | ||
130 | break; | ||
131 | |||
132 | case 4: | ||
133 | if (where & 0x03) { | ||
134 | printk("pci_write_config_dword: misaligned reg [%x]\n", | ||
135 | where); | ||
136 | return PCIBIOS_SUCCESSFUL; | ||
137 | } | ||
138 | pci_config_write32(addr, value); | ||
139 | } | ||
140 | return PCIBIOS_SUCCESSFUL; | ||
141 | } | ||
142 | |||
143 | struct pci_ops sun4u_pci_ops = { | ||
144 | .read = sun4u_read_pci_cfg, | ||
145 | .write = sun4u_write_pci_cfg, | ||
146 | }; | ||
147 | |||
148 | static int sun4v_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, | ||
149 | int where, int size, u32 *value) | ||
150 | { | ||
151 | struct pci_pbm_info *pbm = bus_dev->sysdata; | ||
152 | u32 devhandle = pbm->devhandle; | ||
153 | unsigned int bus = bus_dev->number; | ||
154 | unsigned int device = PCI_SLOT(devfn); | ||
155 | unsigned int func = PCI_FUNC(devfn); | ||
156 | unsigned long ret; | ||
157 | |||
158 | if (bus_dev == pbm->pci_bus && devfn == 0x00) | ||
159 | return pci_host_bridge_read_pci_cfg(bus_dev, devfn, where, | ||
160 | size, value); | ||
161 | if (config_out_of_range(pbm, bus, devfn, where)) { | ||
162 | ret = ~0UL; | ||
163 | } else { | ||
164 | ret = pci_sun4v_config_get(devhandle, | ||
165 | HV_PCI_DEVICE_BUILD(bus, device, func), | ||
166 | where, size); | ||
167 | } | ||
168 | switch (size) { | ||
169 | case 1: | ||
170 | *value = ret & 0xff; | ||
171 | break; | ||
172 | case 2: | ||
173 | *value = ret & 0xffff; | ||
174 | break; | ||
175 | case 4: | ||
176 | *value = ret & 0xffffffff; | ||
177 | break; | ||
178 | }; | ||
179 | |||
180 | |||
181 | return PCIBIOS_SUCCESSFUL; | ||
182 | } | ||
183 | |||
184 | static int sun4v_write_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, | ||
185 | int where, int size, u32 value) | ||
186 | { | ||
187 | struct pci_pbm_info *pbm = bus_dev->sysdata; | ||
188 | u32 devhandle = pbm->devhandle; | ||
189 | unsigned int bus = bus_dev->number; | ||
190 | unsigned int device = PCI_SLOT(devfn); | ||
191 | unsigned int func = PCI_FUNC(devfn); | ||
192 | unsigned long ret; | ||
193 | |||
194 | if (bus_dev == pbm->pci_bus && devfn == 0x00) | ||
195 | return pci_host_bridge_write_pci_cfg(bus_dev, devfn, where, | ||
196 | size, value); | ||
197 | if (config_out_of_range(pbm, bus, devfn, where)) { | ||
198 | /* Do nothing. */ | ||
199 | } else { | ||
200 | ret = pci_sun4v_config_put(devhandle, | ||
201 | HV_PCI_DEVICE_BUILD(bus, device, func), | ||
202 | where, size, value); | ||
203 | } | ||
204 | return PCIBIOS_SUCCESSFUL; | ||
205 | } | ||
206 | |||
207 | struct pci_ops sun4v_pci_ops = { | ||
208 | .read = sun4v_read_pci_cfg, | ||
209 | .write = sun4v_write_pci_cfg, | ||
210 | }; | ||
17 | 211 | ||
18 | void pci_get_pbm_props(struct pci_pbm_info *pbm) | 212 | void pci_get_pbm_props(struct pci_pbm_info *pbm) |
19 | { | 213 | { |