diff options
Diffstat (limited to 'arch/sparc64/kernel/pci_common.c')
-rw-r--r-- | arch/sparc64/kernel/pci_common.c | 123 |
1 files changed, 116 insertions, 7 deletions
diff --git a/arch/sparc64/kernel/pci_common.c b/arch/sparc64/kernel/pci_common.c index 4249214608af..2f61c4b12596 100644 --- a/arch/sparc64/kernel/pci_common.c +++ b/arch/sparc64/kernel/pci_common.c | |||
@@ -44,6 +44,67 @@ static void *sun4u_config_mkaddr(struct pci_pbm_info *pbm, | |||
44 | return (void *) (pbm->config_space | bus | devfn | reg); | 44 | return (void *) (pbm->config_space | bus | devfn | reg); |
45 | } | 45 | } |
46 | 46 | ||
47 | /* At least on Sabre, it is necessary to access all PCI host controller | ||
48 | * registers at their natural size, otherwise zeros are returned. | ||
49 | * Strange but true, and I see no language in the UltraSPARC-IIi | ||
50 | * programmer's manual that mentions this even indirectly. | ||
51 | */ | ||
52 | static int sun4u_read_pci_cfg_host(struct pci_pbm_info *pbm, | ||
53 | unsigned char bus, unsigned int devfn, | ||
54 | int where, int size, u32 *value) | ||
55 | { | ||
56 | u32 tmp32, *addr; | ||
57 | u16 tmp16; | ||
58 | u8 tmp8; | ||
59 | |||
60 | addr = sun4u_config_mkaddr(pbm, bus, devfn, where); | ||
61 | if (!addr) | ||
62 | return PCIBIOS_SUCCESSFUL; | ||
63 | |||
64 | switch (size) { | ||
65 | case 1: | ||
66 | if (where < 8) { | ||
67 | unsigned long align = (unsigned long) addr; | ||
68 | |||
69 | align &= ~1; | ||
70 | pci_config_read16((u16 *)align, &tmp16); | ||
71 | if (where & 1) | ||
72 | *value = tmp16 >> 8; | ||
73 | else | ||
74 | *value = tmp16 & 0xff; | ||
75 | } else { | ||
76 | pci_config_read8((u8 *)addr, &tmp8); | ||
77 | *value = (u32) tmp8; | ||
78 | } | ||
79 | break; | ||
80 | |||
81 | case 2: | ||
82 | if (where < 8) { | ||
83 | pci_config_read16((u16 *)addr, &tmp16); | ||
84 | *value = (u32) tmp16; | ||
85 | } else { | ||
86 | pci_config_read8((u8 *)addr, &tmp8); | ||
87 | *value = (u32) tmp8; | ||
88 | pci_config_read8(((u8 *)addr) + 1, &tmp8); | ||
89 | *value |= ((u32) tmp8) << 8; | ||
90 | } | ||
91 | break; | ||
92 | |||
93 | case 4: | ||
94 | tmp32 = 0xffffffff; | ||
95 | sun4u_read_pci_cfg_host(pbm, bus, devfn, | ||
96 | where, 2, &tmp32); | ||
97 | *value = tmp32; | ||
98 | |||
99 | tmp32 = 0xffffffff; | ||
100 | sun4u_read_pci_cfg_host(pbm, bus, devfn, | ||
101 | where + 2, 2, &tmp32); | ||
102 | *value |= tmp32 << 16; | ||
103 | break; | ||
104 | } | ||
105 | return PCIBIOS_SUCCESSFUL; | ||
106 | } | ||
107 | |||
47 | static int sun4u_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, | 108 | static int sun4u_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, |
48 | int where, int size, u32 *value) | 109 | int where, int size, u32 *value) |
49 | { | 110 | { |
@@ -53,10 +114,6 @@ static int sun4u_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, | |||
53 | u16 tmp16; | 114 | u16 tmp16; |
54 | u8 tmp8; | 115 | u8 tmp8; |
55 | 116 | ||
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) { | 117 | switch (size) { |
61 | case 1: | 118 | case 1: |
62 | *value = 0xff; | 119 | *value = 0xff; |
@@ -69,6 +126,10 @@ static int sun4u_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, | |||
69 | break; | 126 | break; |
70 | } | 127 | } |
71 | 128 | ||
129 | if (!bus_dev->number && !PCI_SLOT(devfn)) | ||
130 | return sun4u_read_pci_cfg_host(pbm, bus, devfn, where, | ||
131 | size, value); | ||
132 | |||
72 | addr = sun4u_config_mkaddr(pbm, bus, devfn, where); | 133 | addr = sun4u_config_mkaddr(pbm, bus, devfn, where); |
73 | if (!addr) | 134 | if (!addr) |
74 | return PCIBIOS_SUCCESSFUL; | 135 | return PCIBIOS_SUCCESSFUL; |
@@ -101,6 +162,53 @@ static int sun4u_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, | |||
101 | return PCIBIOS_SUCCESSFUL; | 162 | return PCIBIOS_SUCCESSFUL; |
102 | } | 163 | } |
103 | 164 | ||
165 | static int sun4u_write_pci_cfg_host(struct pci_pbm_info *pbm, | ||
166 | unsigned char bus, unsigned int devfn, | ||
167 | int where, int size, u32 value) | ||
168 | { | ||
169 | u32 *addr; | ||
170 | |||
171 | addr = sun4u_config_mkaddr(pbm, bus, devfn, where); | ||
172 | if (!addr) | ||
173 | return PCIBIOS_SUCCESSFUL; | ||
174 | |||
175 | switch (size) { | ||
176 | case 1: | ||
177 | if (where < 8) { | ||
178 | unsigned long align = (unsigned long) addr; | ||
179 | u16 tmp16; | ||
180 | |||
181 | align &= ~1; | ||
182 | pci_config_read16((u16 *)align, &tmp16); | ||
183 | if (where & 1) { | ||
184 | tmp16 &= 0x00ff; | ||
185 | tmp16 |= value << 8; | ||
186 | } else { | ||
187 | tmp16 &= 0xff00; | ||
188 | tmp16 |= value; | ||
189 | } | ||
190 | pci_config_write16((u16 *)align, tmp16); | ||
191 | } else | ||
192 | pci_config_write8((u8 *)addr, value); | ||
193 | break; | ||
194 | case 2: | ||
195 | if (where < 8) { | ||
196 | pci_config_write16((u16 *)addr, value); | ||
197 | } else { | ||
198 | pci_config_write8((u8 *)addr, value & 0xff); | ||
199 | pci_config_write8(((u8 *)addr) + 1, value >> 8); | ||
200 | } | ||
201 | break; | ||
202 | case 4: | ||
203 | sun4u_write_pci_cfg_host(pbm, bus, devfn, | ||
204 | where, 2, value & 0xffff); | ||
205 | sun4u_write_pci_cfg_host(pbm, bus, devfn, | ||
206 | where + 2, 2, value >> 16); | ||
207 | break; | ||
208 | } | ||
209 | return PCIBIOS_SUCCESSFUL; | ||
210 | } | ||
211 | |||
104 | static int sun4u_write_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, | 212 | static int sun4u_write_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, |
105 | int where, int size, u32 value) | 213 | int where, int size, u32 value) |
106 | { | 214 | { |
@@ -108,9 +216,10 @@ static int sun4u_write_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, | |||
108 | unsigned char bus = bus_dev->number; | 216 | unsigned char bus = bus_dev->number; |
109 | u32 *addr; | 217 | u32 *addr; |
110 | 218 | ||
111 | if (bus_dev == pbm->pci_bus && devfn == 0x00) | 219 | if (!bus_dev->number && !PCI_SLOT(devfn)) |
112 | return pci_host_bridge_write_pci_cfg(bus_dev, devfn, where, | 220 | return sun4u_write_pci_cfg_host(pbm, bus, devfn, where, |
113 | size, value); | 221 | size, value); |
222 | |||
114 | addr = sun4u_config_mkaddr(pbm, bus, devfn, where); | 223 | addr = sun4u_config_mkaddr(pbm, bus, devfn, where); |
115 | if (!addr) | 224 | if (!addr) |
116 | return PCIBIOS_SUCCESSFUL; | 225 | return PCIBIOS_SUCCESSFUL; |