diff options
Diffstat (limited to 'arch/ppc/boot/simple/misc-spruce.c')
-rw-r--r-- | arch/ppc/boot/simple/misc-spruce.c | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/arch/ppc/boot/simple/misc-spruce.c b/arch/ppc/boot/simple/misc-spruce.c new file mode 100644 index 000000000000..d012c39278fd --- /dev/null +++ b/arch/ppc/boot/simple/misc-spruce.c | |||
@@ -0,0 +1,274 @@ | |||
1 | /* | ||
2 | * arch/ppc/boot/spruce/misc.c | ||
3 | * | ||
4 | * Misc. bootloader code for IBM Spruce reference platform | ||
5 | * | ||
6 | * Authors: Johnnie Peters <jpeters@mvista.com> | ||
7 | * Matt Porter <mporter@mvista.com> | ||
8 | * | ||
9 | * Derived from arch/ppc/boot/prep/misc.c | ||
10 | * | ||
11 | * 2000-2001 (c) MontaVista, Software, Inc. This file is licensed under | ||
12 | * the terms of the GNU General Public License version 2. This program | ||
13 | * is licensed "as is" without any warranty of any kind, whether express | ||
14 | * or implied. | ||
15 | */ | ||
16 | |||
17 | #include <linux/types.h> | ||
18 | #include <linux/config.h> | ||
19 | #include <linux/pci.h> | ||
20 | |||
21 | #include <asm/bootinfo.h> | ||
22 | |||
23 | extern unsigned long decompress_kernel(unsigned long load_addr, int num_words, | ||
24 | unsigned long cksum); | ||
25 | |||
26 | /* Define some important locations of the Spruce. */ | ||
27 | #define SPRUCE_PCI_CONFIG_ADDR 0xfec00000 | ||
28 | #define SPRUCE_PCI_CONFIG_DATA 0xfec00004 | ||
29 | |||
30 | /* PCI configuration space access routines. */ | ||
31 | unsigned int *pci_config_address = (unsigned int *)SPRUCE_PCI_CONFIG_ADDR; | ||
32 | unsigned char *pci_config_data = (unsigned char *)SPRUCE_PCI_CONFIG_DATA; | ||
33 | |||
34 | void cpc700_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, | ||
35 | unsigned char offset, unsigned char *val) | ||
36 | { | ||
37 | out_le32(pci_config_address, | ||
38 | (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); | ||
39 | |||
40 | *val= (in_le32((unsigned *)pci_config_data) >> (8 * (offset & 3))) & 0xff; | ||
41 | } | ||
42 | |||
43 | void cpc700_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, | ||
44 | unsigned char offset, unsigned char val) | ||
45 | { | ||
46 | out_le32(pci_config_address, | ||
47 | (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); | ||
48 | |||
49 | out_8(pci_config_data + (offset&3), val); | ||
50 | } | ||
51 | |||
52 | void cpc700_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, | ||
53 | unsigned char offset, unsigned short *val) | ||
54 | { | ||
55 | out_le32(pci_config_address, | ||
56 | (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); | ||
57 | |||
58 | *val= in_le16((unsigned short *)(pci_config_data + (offset&3))); | ||
59 | } | ||
60 | |||
61 | void cpc700_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, | ||
62 | unsigned char offset, unsigned short val) | ||
63 | { | ||
64 | out_le32(pci_config_address, | ||
65 | (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); | ||
66 | |||
67 | out_le16((unsigned short *)(pci_config_data + (offset&3)), val); | ||
68 | } | ||
69 | |||
70 | void cpc700_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, | ||
71 | unsigned char offset, unsigned int *val) | ||
72 | { | ||
73 | out_le32(pci_config_address, | ||
74 | (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); | ||
75 | |||
76 | *val= in_le32((unsigned *)pci_config_data); | ||
77 | } | ||
78 | |||
79 | void cpc700_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, | ||
80 | unsigned char offset, unsigned int val) | ||
81 | { | ||
82 | out_le32(pci_config_address, | ||
83 | (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); | ||
84 | |||
85 | out_le32((unsigned *)pci_config_data, val); | ||
86 | } | ||
87 | |||
88 | #define PCNET32_WIO_RDP 0x10 | ||
89 | #define PCNET32_WIO_RAP 0x12 | ||
90 | #define PCNET32_WIO_RESET 0x14 | ||
91 | |||
92 | #define PCNET32_DWIO_RDP 0x10 | ||
93 | #define PCNET32_DWIO_RAP 0x14 | ||
94 | #define PCNET32_DWIO_RESET 0x18 | ||
95 | |||
96 | /* Processor interface config register access */ | ||
97 | #define PIFCFGADDR 0xff500000 | ||
98 | #define PIFCFGDATA 0xff500004 | ||
99 | |||
100 | #define PLBMIFOPT 0x18 /* PLB Master Interface Options */ | ||
101 | |||
102 | #define MEM_MBEN 0x24 | ||
103 | #define MEM_TYPE 0x28 | ||
104 | #define MEM_B1SA 0x3c | ||
105 | #define MEM_B1EA 0x5c | ||
106 | #define MEM_B2SA 0x40 | ||
107 | #define MEM_B2EA 0x60 | ||
108 | |||
109 | unsigned long | ||
110 | get_mem_size(void) | ||
111 | { | ||
112 | int loop; | ||
113 | unsigned long mem_size = 0; | ||
114 | unsigned long mem_mben; | ||
115 | unsigned long mem_type; | ||
116 | unsigned long mem_start; | ||
117 | unsigned long mem_end; | ||
118 | volatile int *mem_addr = (int *)0xff500008; | ||
119 | volatile int *mem_data = (int *)0xff50000c; | ||
120 | |||
121 | /* Get the size of memory from the memory controller. */ | ||
122 | *mem_addr = MEM_MBEN; | ||
123 | asm("sync"); | ||
124 | mem_mben = *mem_data; | ||
125 | asm("sync"); | ||
126 | for(loop = 0; loop < 1000; loop++); | ||
127 | |||
128 | *mem_addr = MEM_TYPE; | ||
129 | asm("sync"); | ||
130 | mem_type = *mem_data; | ||
131 | asm("sync"); | ||
132 | for(loop = 0; loop < 1000; loop++); | ||
133 | |||
134 | *mem_addr = MEM_TYPE; | ||
135 | /* Confirm bank 1 has DRAM memory */ | ||
136 | if ((mem_mben & 0x40000000) && | ||
137 | ((mem_type & 0x30000000) == 0x10000000)) { | ||
138 | *mem_addr = MEM_B1SA; | ||
139 | asm("sync"); | ||
140 | mem_start = *mem_data; | ||
141 | asm("sync"); | ||
142 | for(loop = 0; loop < 1000; loop++); | ||
143 | |||
144 | *mem_addr = MEM_B1EA; | ||
145 | asm("sync"); | ||
146 | mem_end = *mem_data; | ||
147 | asm("sync"); | ||
148 | for(loop = 0; loop < 1000; loop++); | ||
149 | |||
150 | mem_size = mem_end - mem_start + 0x100000; | ||
151 | } | ||
152 | |||
153 | /* Confirm bank 2 has DRAM memory */ | ||
154 | if ((mem_mben & 0x20000000) && | ||
155 | ((mem_type & 0xc000000) == 0x4000000)) { | ||
156 | *mem_addr = MEM_B2SA; | ||
157 | asm("sync"); | ||
158 | mem_start = *mem_data; | ||
159 | asm("sync"); | ||
160 | for(loop = 0; loop < 1000; loop++); | ||
161 | |||
162 | *mem_addr = MEM_B2EA; | ||
163 | asm("sync"); | ||
164 | mem_end = *mem_data; | ||
165 | asm("sync"); | ||
166 | for(loop = 0; loop < 1000; loop++); | ||
167 | |||
168 | mem_size += mem_end - mem_start + 0x100000; | ||
169 | } | ||
170 | return mem_size; | ||
171 | } | ||
172 | |||
173 | unsigned long | ||
174 | load_kernel(unsigned long load_addr, int num_words, unsigned long cksum, | ||
175 | void *ign1, void *ign2) | ||
176 | { | ||
177 | int csr0; | ||
178 | int csr_id; | ||
179 | int pci_devfn; | ||
180 | int found_multi = 0; | ||
181 | unsigned short vendor; | ||
182 | unsigned short device; | ||
183 | unsigned short command; | ||
184 | unsigned char header_type; | ||
185 | unsigned int bar0; | ||
186 | volatile int *pif_addr = (int *)0xff500000; | ||
187 | volatile int *pif_data = (int *)0xff500004; | ||
188 | |||
189 | /* | ||
190 | * Gah, these firmware guys need to learn that hardware | ||
191 | * byte swapping is evil! Disable all hardware byte | ||
192 | * swapping so it doesn't hurt anyone. | ||
193 | */ | ||
194 | *pif_addr = PLBMIFOPT; | ||
195 | asm("sync"); | ||
196 | *pif_data = 0x00000000; | ||
197 | asm("sync"); | ||
198 | |||
199 | /* Search out and turn off the PcNet ethernet boot device. */ | ||
200 | for (pci_devfn = 1; pci_devfn < 0xff; pci_devfn++) { | ||
201 | if (PCI_FUNC(pci_devfn) && !found_multi) | ||
202 | continue; | ||
203 | |||
204 | cpc700_pcibios_read_config_byte(0, pci_devfn, | ||
205 | PCI_HEADER_TYPE, &header_type); | ||
206 | |||
207 | if (!PCI_FUNC(pci_devfn)) | ||
208 | found_multi = header_type & 0x80; | ||
209 | |||
210 | cpc700_pcibios_read_config_word(0, pci_devfn, PCI_VENDOR_ID, | ||
211 | &vendor); | ||
212 | |||
213 | if (vendor != 0xffff) { | ||
214 | cpc700_pcibios_read_config_word(0, pci_devfn, | ||
215 | PCI_DEVICE_ID, &device); | ||
216 | |||
217 | /* If this PCI device is the Lance PCNet board then turn it off */ | ||
218 | if ((vendor == PCI_VENDOR_ID_AMD) && | ||
219 | (device == PCI_DEVICE_ID_AMD_LANCE)) { | ||
220 | |||
221 | /* Turn on I/O Space on the board. */ | ||
222 | cpc700_pcibios_read_config_word(0, pci_devfn, | ||
223 | PCI_COMMAND, &command); | ||
224 | command |= 0x1; | ||
225 | cpc700_pcibios_write_config_word(0, pci_devfn, | ||
226 | PCI_COMMAND, command); | ||
227 | |||
228 | /* Get the I/O space address */ | ||
229 | cpc700_pcibios_read_config_dword(0, pci_devfn, | ||
230 | PCI_BASE_ADDRESS_0, &bar0); | ||
231 | bar0 &= 0xfffffffe; | ||
232 | |||
233 | /* Reset the PCNet Board */ | ||
234 | inl (bar0+PCNET32_DWIO_RESET); | ||
235 | inw (bar0+PCNET32_WIO_RESET); | ||
236 | |||
237 | /* First do a work oriented read of csr0. If the value is | ||
238 | * 4 then this is the correct mode to access the board. | ||
239 | * If not try a double word ortiented read. | ||
240 | */ | ||
241 | outw(0, bar0 + PCNET32_WIO_RAP); | ||
242 | csr0 = inw(bar0 + PCNET32_WIO_RDP); | ||
243 | |||
244 | if (csr0 == 4) { | ||
245 | /* Check the Chip id register */ | ||
246 | outw(88, bar0 + PCNET32_WIO_RAP); | ||
247 | csr_id = inw(bar0 + PCNET32_WIO_RDP); | ||
248 | |||
249 | if (csr_id) { | ||
250 | /* This is the valid mode - set the stop bit */ | ||
251 | outw(0, bar0 + PCNET32_WIO_RAP); | ||
252 | outw(csr0, bar0 + PCNET32_WIO_RDP); | ||
253 | } | ||
254 | } else { | ||
255 | outl(0, bar0 + PCNET32_DWIO_RAP); | ||
256 | csr0 = inl(bar0 + PCNET32_DWIO_RDP); | ||
257 | if (csr0 == 4) { | ||
258 | /* Check the Chip id register */ | ||
259 | outl(88, bar0 + PCNET32_WIO_RAP); | ||
260 | csr_id = inl(bar0 + PCNET32_WIO_RDP); | ||
261 | |||
262 | if (csr_id) { | ||
263 | /* This is the valid mode - set the stop bit*/ | ||
264 | outl(0, bar0 + PCNET32_WIO_RAP); | ||
265 | outl(csr0, bar0 + PCNET32_WIO_RDP); | ||
266 | } | ||
267 | } | ||
268 | } | ||
269 | } | ||
270 | } | ||
271 | } | ||
272 | |||
273 | return decompress_kernel(load_addr, num_words, cksum); | ||
274 | } | ||