summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/powerpc/mvme5500/pci/pci.c
blob: 3afb214ea43961bf7d914790bcc0f0f06bb57d31 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
/*
 * pci.c :  this file contains basic PCI Io functions.
 *
 *  CopyRight (C) 1999 valette@crf.canon.fr
 *
 *  This code is heavilly inspired by the public specification of STREAM V2
 *  that can be found at :
 *
 *      <http://www.chorus.com/Documentation/index.html> by following
 *  the STREAM API Specification Document link.
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.com/license/LICENSE.
 *
 *  $Id$
 *
 *  Copyright 2004, Brookhaven National Laboratory and
 *                  Shuchen K. Feng, <feng1@bnl.gov>, 2004
 *   - modified and added support for MVME5500 board
 *   - added 2nd PCI support for the mvme5500/GT64260 PCI bridge
 *
 */
#define PCI_MAIN

#include <libcpu/io.h>
#include <rtems/bspIo.h>	    /* printk */

#include <bsp/pci.h>
#include <bsp/gtreg.h>
#include <bsp/gtpcireg.h> 

#include <stdio.h>
#include <string.h>

#define PCI_DEBUG 0
#define PCI_PRINT 1

#define PCI_INVALID_VENDORDEVICEID	0xffffffff
#define PCI_MULTI_FUNCTION		0x80
#define HOSTBRIDGET_ERROR               0xf0000000

typedef unsigned char unchar;

#define	MAX_NUM_PCI_DEVICES	20

static int		  numPCIDevs=0;
extern void PCI_interface(), pciAccessInit();

/* Pack RegNum,FuncNum,DevNum,BusNum,and ConfigEnable for
 * PCI Configuration Address Register
 */
#define pciConfigPack(bus,dev,func,offset)\
(((func&7)<<8)|((dev&0x1f )<<11)|(( bus&0xff)<<16)|(offset&0xfc))|0x80000000

/*
 * Bit encode for PCI_CONFIG_HEADER_TYPE register
 */
unchar ucMaxPCIBus=0;

/* Please note that PCI0 and PCI1 does not correlate with the busNum 0 and 1.
 */
int PCIx_read_config_byte(int npci, unchar bus, unchar dev,
unchar func, unchar offset, unchar *val)
{
  *val = 0xff;
  if (offset & ~0xff) return PCIBIOS_BAD_REGISTER_NUMBER;
  outl(pciConfigPack(bus,dev,func,offset),BSP_pci_config[npci].pci_config_addr);
  *val = inb(BSP_pci_config[npci].pci_config_data + (offset&3));
  return PCIBIOS_SUCCESSFUL;
}

int PCIx_read_config_word(int npci, unchar bus, unchar dev,
unchar func, unchar offset, unsigned short *val)
{
  *val = 0xffff; 
  if ((offset&1)|| (offset & ~0xff)) return PCIBIOS_BAD_REGISTER_NUMBER;
  outl(pciConfigPack(bus,dev,func,offset),BSP_pci_config[npci].pci_config_addr);
  *val = inw(BSP_pci_config[npci].pci_config_data + (offset&2));
  return PCIBIOS_SUCCESSFUL;
}

int PCIx_read_config_dword(int npci, unchar bus, unchar dev,
unchar func, unchar offset, unsigned int *val) 
{
  *val = 0xffffffff; 
  if ((offset&3)|| (offset & ~0xff)) return PCIBIOS_BAD_REGISTER_NUMBER;
#if 0
  printk("addr %x, data %x, pack %x \n", BSP_pci_config[npci].pci_config_addr,
    BSP_pci_config[npci].pci_config_data,pciConfigPack(bus,dev,func,offset)); 
#endif
  outl(pciConfigPack(bus,dev,func,offset),BSP_pci_config[npci].pci_config_addr);
  *val = inl(BSP_pci_config[npci].pci_config_data);
  return PCIBIOS_SUCCESSFUL;
}

int PCIx_write_config_byte(int npci, unchar bus, unchar dev,
unchar func, unchar offset, unchar val) 
{
  if (offset & ~0xff) return PCIBIOS_BAD_REGISTER_NUMBER;

  outl(pciConfigPack(bus,dev,func,offset),BSP_pci_config[npci].pci_config_addr);
  outb(val, BSP_pci_config[npci].pci_config_data + (offset&3));
  return PCIBIOS_SUCCESSFUL;
}

int PCIx_write_config_word(int npci, unchar bus, unchar dev,
unchar func, unchar offset, unsigned short val) 
{
  if ((offset&1)|| (offset & ~0xff)) return PCIBIOS_BAD_REGISTER_NUMBER;
  outl(pciConfigPack(bus,dev,func,offset),BSP_pci_config[npci].pci_config_addr);
  outw(val, BSP_pci_config[npci].pci_config_data + (offset&3));
  return PCIBIOS_SUCCESSFUL;
}

int PCIx_write_config_dword(int npci,unchar bus,unchar dev,
unchar func, unchar offset, unsigned int val) 
{
  if ((offset&3)|| (offset & ~0xff)) return PCIBIOS_BAD_REGISTER_NUMBER;
#if 0
  printk("addr %x, data %x, pack %x \n", BSP_pci_config[npci].pci_config_addr,
    BSP_pci_config[npci].pci_config_data,pciConfigPack(bus,dev,func,offset));
#endif
  outl(pciConfigPack(bus,dev,func,offset),BSP_pci_config[npci].pci_config_addr);
  outl(val,BSP_pci_config[npci].pci_config_data);
  return PCIBIOS_SUCCESSFUL;
}

/* backwards compatible with other PPC board for the vmeUniverse.c 
 * Note: We must override the default with these in pci.h
 */
int pci_bsp_read_config_byte(unchar bus, unchar dev,unchar func,unchar offset,
unchar *val)
{
  return(PCIx_read_config_byte(0, bus, dev, func, offset, val));
}

int pci_bsp_read_config_word(unchar bus, unchar dev,
unchar func, unchar offset, unsigned short *val)
{
  return(PCIx_read_config_word(0, bus, dev, func, offset, val));
}

int pci_bsp_read_config_dword(unchar bus, unchar dev,
unchar func, unchar offset, unsigned int *val) 
{
  return(PCIx_read_config_dword(0, bus, dev, func, offset, val));
}

int pci_bsp_write_config_byte(unchar bus, unchar dev,
unchar func, unchar offset, unchar val) 
{
  return(PCIx_write_config_byte(0, bus, dev, func, offset, val));
}

int pci_bsp_write_config_word(unchar bus, unchar dev,
unchar func, unchar offset, unsigned short val) 
{
  return(PCIx_write_config_word(0, bus, dev, func, offset, val));
}

int pci_bsp_write_config_dword(unchar bus,unchar dev,
unchar func, unchar offset, unsigned int val) 
{
  return(PCIx_write_config_dword(0, bus, dev, func, offset, val));
}


pci_bsp_config BSP_pci_config[2] = {
  {PCI0_CONFIG_ADDR,PCI0_CONFIG_DATA/*,&pci_functions*/},
       {PCI1_CONFIG_ADDR,PCI1_CONFIG_DATA/*,&pci_functions*/}
};

/*
 * This routine determines the maximum bus number in the system
 */
int pci_initialize()
{
  int PciNumber;
  unchar ucBusNumber, ucSlotNumber, ucFnNumber, ucNumFuncs;
  unsigned int ulHeader;
  unsigned int pcidata, ulDeviceID;
#if PCI_DEBUG
  unsigned int data, pcidata, ulClass;
  unsigned short sdata;
#endif

  PCI_interface();
  
  /*
   * Scan PCI0 and PCI1 bus0
   */
  for (PciNumber=0; PciNumber < 2; PciNumber++) {
    pciAccessInit(PciNumber);
    for (ucBusNumber=0; ucBusNumber< 2; ucBusNumber++) {
    for (ucSlotNumber=0;ucSlotNumber<PCI_MAX_DEVICES;ucSlotNumber++) {
      ucFnNumber = 0;
      PCIx_read_config_dword(PciNumber, ucBusNumber,
				ucSlotNumber,
				0,
				PCI0_VENDOR_ID,
				&ulDeviceID);

      if( ulDeviceID==PCI_INVALID_VENDORDEVICEID) {
        /* This slot is empty */
        continue;
      }

      if (++numPCIDevs > MAX_NUM_PCI_DEVICES) {
	 BSP_panic("Too many PCI devices found; increase MAX_NUM_PCI_DEVICES in pcicache.c\n");
      }

      switch(ulDeviceID) { 
        case (PCI_VENDOR_ID_MARVELL+(PCI_DEVICE_ID_MARVELL_GT6426xAB<<16)):
#if PCI_PRINT
	  printk("Marvell GT6426xA/B hostbridge detected at PCI%d bus%d slot%d\n",
                 PciNumber,ucBusNumber,ucSlotNumber);
#endif
          ucMaxPCIBus ++;
	  break;
        case (PCI_VENDOR_ID_PLX2+(PCI_DEVICE_ID_PLX2_PCI6154_HB2<<16)):
#if PCI_PRINT
          printk("PLX PCI6154 PCI-PCI bridge detected at PCI%d bus%d slot%d\n",
                 PciNumber,ucBusNumber,ucSlotNumber);
#endif
          ucMaxPCIBus ++;
          break;
        case PCI_VENDOR_ID_TUNDRA:
#if PCI_PRINT
          printk("TUNDRA PCI-VME bridge detected at PCI%d bus%d slot%d\n",
                 PciNumber,ucBusNumber,ucSlotNumber);
#endif
          ucMaxPCIBus ++;
          break;
      case (PCI_VENDOR_ID_INTEL+(PCI_DEVICE_INTEL_82544EI_COPPER<<16)):
#if PCI_PRINT
          printk("INTEL 82544EI COPPER network controller detected at PCI%d bus%d slot%d\n",
                 PciNumber,ucBusNumber,ucSlotNumber);
#endif
          ucMaxPCIBus ++;
          break;
        default : 
#if PCI_PRINT
          printk("PCI%d Bus%d Slot%d DeviceID 0x%x \n",
             PciNumber,ucBusNumber,ucSlotNumber, ulDeviceID);
#endif
          break;
      }
#if PCI_DEBUG
      PCIx_read_config_dword(PciNumber, ucBusNumber,
			  ucSlotNumber,
			  0,
			  PCI0_BASE_ADDRESS_0,
                          &data);
      printk("PCI%d_BASE_ADDRESS_0 0x%x \n",PciNumber, data);  
      PCIx_read_config_dword(PciNumber, ucBusNumber,
			  ucSlotNumber,
			  0,
			  PCI0_BASE_ADDRESS_1,
                          &data);
      printk("PCI%d_BASE_ADDRESS_1 0x%x \n",PciNumber, data);
      PCIx_read_config_dword(PciNumber, ucBusNumber,
 			  ucSlotNumber,
			  0,
			  PCI0_BASE_ADDRESS_2,
                          &data);
      printk("PCI%d_BASE_ADDRESS_2 0x%x \n",PciNumber, data);

      PCIx_read_config_dword(PciNumber, ucBusNumber,
 			  ucSlotNumber,
			  0,
			  PCI0_BASE_ADDRESS_3,
                          &data);
      printk("PCI%d_BASE_ADDRESS_3 0x%x \n",PciNumber, data);  

     PCIx_read_config_word(PciNumber, ucBusNumber,
 			  ucSlotNumber,
			  0,
			  PCI0_INTERRUPT_LINE,
                          &sdata);
      printk("PCI%d_INTERRUPT_LINE 0x%x \n",PciNumber, sdata);  

      /* We always enable internal memory. */
      PCIx_read_config_dword(PciNumber, ucBusNumber,
 			  ucSlotNumber,
			  0,
			  PCI0_MEM_BASE_ADDR,
			  &pcidata);
      printk("PCI%d_MEM_BASE_ADDR 0x%x \n", PciNumber,pcidata);

      /* We always enable internal IO. */
      PCIx_read_config_dword(PciNumber, ucBusNumber,
 			  ucSlotNumber,
			  0,
			  PCI0_IO_BASE_ADDR,
			  &pcidata);
      printk("PCI%d_IO_BASE_ADDR 0x%x \n", PciNumber,pcidata);
#endif

      PCIx_read_config_dword(PciNumber, ucBusNumber,
			  ucSlotNumber,
			  0,
			  PCI0_CACHE_LINE_SIZE,
			  &ulHeader);
      if ((ulHeader>>16)&PCI_MULTI_FUNCTION)
         ucNumFuncs=PCI_MAX_FUNCTIONS;
      else
         ucNumFuncs=1;

#if PCI_DEBUG
      printk("PCI%d Slot 0x%x HEADER/LAT/CACHE 0x%x \n",
             PciNumber,ucSlotNumber, ulHeader);    

      for (ucFnNumber=1;ucFnNumber<ucNumFuncs;ucFnNumber++) {
          PCIx_read_config_dword(PciNumber, ucBusNumber,
				  ucSlotNumber,
				  ucFnNumber,
				  PCI0_VENDOR_ID,
				  &ulDeviceID);
          if (ulDeviceID==PCI_INVALID_VENDORDEVICEID) {
	     /* This slot/function is empty */
	     continue;
          }
          if (++numPCIDevs > MAX_NUM_PCI_DEVICES) {
	     BSP_panic("Too many PCI devices found; increase MAX_NUM_PCI_DEVICES in pcicache.c\n");
          }

         /* This slot/function has a device fitted.*/
         PCIx_read_config_dword(PciNumber, ucBusNumber,
				  ucSlotNumber,
				  ucFnNumber,
				  PCI0_CLASS_REVISION,
				  &ulClass);     
         printk("PCI%d Slot 0x%x Func %d classID 0x%x \n",PciNumber,ucSlotNumber,
             ucFnNumber, ulClass);

         ulClass >>= 16;
         if (ulClass == PCI_CLASS_GT6426xAB)
	    printk("GT64260-PCI%d bridge found \n", PciNumber);
      }
#endif
      PCIx_read_config_dword(PciNumber, ucBusNumber,
			       ucSlotNumber,
			       0,
			  PCI0_COMMAND,
                          &pcidata); 
#if PCI_DEBUG
      printk("MOTLoad command staus 0x%x, ", pcidata);
#endif
      /* Clear the error on the host bridge */
      if ( (ucBusNumber==0) && (ucSlotNumber==0))
	pcidata |= PCI_STATUS_CLRERR_MASK;
      /* Enable bus,I/O and memory master access. */
      pcidata |= (PCI_COMMAND_MASTER|PCI_COMMAND_IO|PCI_COMMAND_MEMORY);
      PCIx_write_config_dword(PciNumber, ucBusNumber,
 			       ucSlotNumber,
			       0,
			  PCI0_COMMAND,
                          pcidata);

      PCIx_read_config_dword(PciNumber, ucBusNumber,
			       ucSlotNumber,
			       0,
			  PCI0_COMMAND,
                          &pcidata); 
#if PCI_DEBUG      
      printk("Now command/staus 0x%x\n", pcidata);
#endif
 
    }
    }
  } /* PCI number */

  return PCIB_ERR_SUCCESS;
}

/*
 * Return the number of PCI buses in the system
 */
unsigned char pci_bus_count()
{
  return(ucMaxPCIBus);
}