summaryrefslogtreecommitdiff
path: root/cpu/ixp/pci.c
blob: 8c6b0b21b48642efd5fa639ebadafaa25b3b52a7 (plain)
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
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
/*
 * IXP PCI Init
 * (C) Copyright 2004 eslab.whut.edu.cn
 * Yue Hu(huyue_whut@yahoo.com.cn), Ligong Xue(lgxue@hotmail.com)
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */


#include <common.h>

#ifdef CONFIG_PCI

#include <asm/processor.h>
#include <asm/io.h>
#include <pci.h>
#include <asm/arch/ixp425.h>
#include <asm/arch/ixp425pci.h>

static void non_prefetch_read (unsigned int addr, unsigned int cmd,
			       unsigned int *data);
static void non_prefetch_write (unsigned int addr, unsigned int cmd,
				unsigned int data);
static void configure_pins (void);
static void sys_pci_gpio_clock_config (void);
static void pci_bus_scan (void);
static int pci_device_exists (unsigned int deviceNo);
static void sys_pci_bar_info_get (unsigned int devnum, unsigned int bus,
				  unsigned int dev, unsigned int func);
static void sys_pci_device_bars_write (void);
static void calc_bars (PciBar * Bars[], unsigned int nBars,
		       unsigned int startAddr);

#define PCI_MEMORY_BUS		0x00000000
#define PCI_MEMORY_PHY		0x48000000
#define PCI_MEMORY_SIZE		0x04000000

#define PCI_MEM_BUS		0x40000000
#define PCI_MEM_PHY		0x00000000
#define PCI_MEM_SIZE		0x04000000

#define	PCI_IO_BUS		0x40000000
#define PCI_IO_PHY		0x50000000
#define PCI_IO_SIZE		0x10000000

struct pci_controller hose;

unsigned int nDevices;
unsigned int nMBars;
unsigned int nIOBars;
PciBar *memBars[IXP425_PCI_MAX_BAR];
PciBar *ioBars[IXP425_PCI_MAX_BAR];
PciDevice devices[IXP425_PCI_MAX_FUNC_ON_BUS];

int pci_read_config_dword (pci_dev_t dev, int where, unsigned int *val)
{
	unsigned int retval;
	unsigned int addr;

	/*address bits 31:28 specify the device 10:8 specify the function */
	/*Set the address to be read */
	addr = BIT ((31 - dev)) | (where & ~3);
	non_prefetch_read (addr, NP_CMD_CONFIGREAD, &retval);

	*val = retval;

	return (OK);
}

int pci_read_config_word (pci_dev_t dev, int where, unsigned short *val)
{
	unsigned int n;
	unsigned int retval;
	unsigned int addr;
	unsigned int byteEnables;

	n = where % 4;
	/*byte enables are 4 bits active low, the position of each
	   bit maps to the byte that it enables */
	byteEnables =
		(~(BIT (n) | BIT ((n + 1)))) &
		IXP425_PCI_BOTTOM_NIBBLE_OF_LONG_MASK;
	byteEnables = byteEnables << PCI_NP_CBE_BESL;
	/*address bits 31:28 specify the device 10:8 specify the function */
	/*Set the address to be read */
	addr = BIT ((31 - dev)) | (where & ~3);
	non_prefetch_read (addr, byteEnables | NP_CMD_CONFIGREAD, &retval);

	/*Pick out the word we are interested in */
	*val = (retval >> (8 * n));

	return (OK);
}

int pci_read_config_byte (pci_dev_t dev, int where, unsigned char *val)
{
	unsigned int retval;
	unsigned int n;
	unsigned int byteEnables;
	unsigned int addr;

	n = where % 4;
	/*byte enables are 4 bits, active low, the position of each
	   bit maps to the byte that it enables */
	byteEnables = (~BIT (n)) & IXP425_PCI_BOTTOM_NIBBLE_OF_LONG_MASK;
	byteEnables = byteEnables << PCI_NP_CBE_BESL;

	/*address bits 31:28 specify the device, 10:8 specify the function */
	/*Set the address to be read */
	addr = BIT ((31 - dev)) | (where & ~3);
	non_prefetch_read (addr, byteEnables | NP_CMD_CONFIGREAD, &retval);
	/*Pick out the byte we are interested in */
	*val = (retval >> (8 * n));

	return (OK);
}

int pci_write_config_byte (pci_dev_t dev, int where, unsigned char val)
{
	unsigned int addr;
	unsigned int byteEnables;
	unsigned int n;
	unsigned int ldata;

	n = where % 4;
	/*byte enables are 4 bits active low, the position of each
	   bit maps to the byte that it enables */
	byteEnables = (~BIT (n)) & IXP425_PCI_BOTTOM_NIBBLE_OF_LONG_MASK;
	byteEnables = byteEnables << PCI_NP_CBE_BESL;
	ldata = val << (8 * n);
	/*address bits 31:28 specify the device 10:8 specify the function */
	/*Set the address to be written */
	addr = BIT ((31 - dev)) | (where & ~3);
	non_prefetch_write (addr, byteEnables | NP_CMD_CONFIGWRITE, ldata);

	return (OK);
}

int pci_write_config_word (pci_dev_t dev, int where, unsigned short val)
{
	unsigned int addr;
	unsigned int byteEnables;
	unsigned int n;
	unsigned int ldata;

	n = where % 4;
	/*byte enables are 4 bits active low, the position of each
	   bit maps to the byte that it enables */
	byteEnables =
		(~(BIT (n) | BIT ((n + 1)))) &
		IXP425_PCI_BOTTOM_NIBBLE_OF_LONG_MASK;
	byteEnables = byteEnables << PCI_NP_CBE_BESL;
	ldata = val << (8 * n);
	/*address bits 31:28 specify the device 10:8 specify the function */
	/*Set the address to be written */
	addr = BIT (31 - dev) | (where & ~3);
	non_prefetch_write (addr, byteEnables | NP_CMD_CONFIGWRITE, ldata);

	return (OK);
}

int pci_write_config_dword (pci_dev_t dev, int where, unsigned int val)
{
	unsigned int addr;

	/*address bits 31:28 specify the device 10:8 specify the function */
	/*Set the address to be written */
	addr = BIT (31 - dev) | (where & ~3);
	non_prefetch_write (addr, NP_CMD_CONFIGWRITE, val);

	return (OK);
}

void non_prefetch_read (unsigned int addr,
			unsigned int cmd, unsigned int *data)
{
	REG_WRITE (PCI_CSR_BASE, PCI_NP_AD_OFFSET, addr);

	/*set up and execute the read */
	REG_WRITE (PCI_CSR_BASE, PCI_NP_CBE_OFFSET, cmd);

	/*The result of the read is now in np_rdata */
	REG_READ (PCI_CSR_BASE, PCI_NP_RDATA_OFFSET, *data);

	return;
}

void non_prefetch_write (unsigned int addr,
			 unsigned int cmd, unsigned int data)
{

	REG_WRITE (PCI_CSR_BASE, PCI_NP_AD_OFFSET, addr);
	/*set up the write */
	REG_WRITE (PCI_CSR_BASE, PCI_NP_CBE_OFFSET, cmd);
	/*Execute the write by writing to NP_WDATA */
	REG_WRITE (PCI_CSR_BASE, PCI_NP_WDATA_OFFSET, data);

	return;
}

/*
 * PCI controller config registers are accessed through these functions
 * i.e. these allow us to set up our own BARs etc.
 */
void crp_read (unsigned int offset, unsigned int *data)
{
	REG_WRITE (PCI_CSR_BASE, PCI_CRP_AD_CBE_OFFSET, offset);
	REG_READ (PCI_CSR_BASE, PCI_CRP_RDATA_OFFSET, *data);
}

void crp_write (unsigned int offset, unsigned int data)
{
	/*The CRP address register bit 16 indicates that we want to do a write */
	REG_WRITE (PCI_CSR_BASE, PCI_CRP_AD_CBE_OFFSET,
		   PCI_CRP_WRITE | offset);
	REG_WRITE (PCI_CSR_BASE, PCI_CRP_WDATA_OFFSET, data);
}

/*struct pci_controller *hose*/
void pci_ixp_init (struct pci_controller *hose)
{
	unsigned int regval;

	hose->first_busno = 0;
	hose->last_busno = 0x00;

	/* System memory space */
	pci_set_region (hose->regions + 0,
			PCI_MEMORY_BUS,
			PCI_MEMORY_PHY, PCI_MEMORY_SIZE, PCI_REGION_MEMORY);

	/* PCI memory space */
	pci_set_region (hose->regions + 1,
			PCI_MEM_BUS,
			PCI_MEM_PHY, PCI_MEM_SIZE, PCI_REGION_MEM);
	/* PCI I/O space */
	pci_set_region (hose->regions + 2,
			PCI_IO_BUS, PCI_IO_PHY, PCI_IO_SIZE, PCI_REGION_IO);

	hose->region_count = 3;

	pci_register_hose (hose);

/*
 ==========================================================
		Init IXP PCI
 ==========================================================
*/
	REG_READ (PCI_CSR_BASE, PCI_CSR_OFFSET, regval);
	regval |= 1 << 2;
	REG_WRITE (PCI_CSR_BASE, PCI_CSR_OFFSET, regval);

	configure_pins ();

	READ_GPIO_REG (IXP425_GPIO_GPOUTR, regval);
	WRITE_GPIO_REG (IXP425_GPIO_GPOUTR, regval & (~(1 << 13)));
	udelay (533);
	sys_pci_gpio_clock_config ();
	REG_WRITE (PCI_CSR_BASE, PCI_INTEN_OFFSET, 0);
	udelay (100);
	READ_GPIO_REG (IXP425_GPIO_GPOUTR, regval);
	WRITE_GPIO_REG (IXP425_GPIO_GPOUTR, regval | (1 << 13));
	udelay (533);
	crp_write (PCI_CFG_BASE_ADDRESS_0, IXP425_PCI_BAR_0_DEFAULT);
	crp_write (PCI_CFG_BASE_ADDRESS_1, IXP425_PCI_BAR_1_DEFAULT);
	crp_write (PCI_CFG_BASE_ADDRESS_2, IXP425_PCI_BAR_2_DEFAULT);
	crp_write (PCI_CFG_BASE_ADDRESS_3, IXP425_PCI_BAR_3_DEFAULT);
	crp_write (PCI_CFG_BASE_ADDRESS_4, IXP425_PCI_BAR_4_DEFAULT);
	crp_write (PCI_CFG_BASE_ADDRESS_5, IXP425_PCI_BAR_5_DEFAULT);
	/*Setup PCI-AHB and AHB-PCI address mappings */
	REG_WRITE (PCI_CSR_BASE, PCI_AHBMEMBASE_OFFSET,
		   IXP425_PCI_AHBMEMBASE_DEFAULT);

	REG_WRITE (PCI_CSR_BASE, PCI_AHBIOBASE_OFFSET,
		   IXP425_PCI_AHBIOBASE_DEFAULT);

	REG_WRITE (PCI_CSR_BASE, PCI_PCIMEMBASE_OFFSET,
		   IXP425_PCI_PCIMEMBASE_DEFAULT);

	crp_write (PCI_CFG_SUB_VENDOR_ID, IXP425_PCI_SUB_VENDOR_SYSTEM);

	REG_READ (PCI_CSR_BASE, PCI_CSR_OFFSET, regval);
	regval |= PCI_CSR_IC | PCI_CSR_ABE | PCI_CSR_PDS;
	REG_WRITE (PCI_CSR_BASE, PCI_CSR_OFFSET, regval);
	crp_write (PCI_CFG_COMMAND, PCI_CFG_CMD_MAE | PCI_CFG_CMD_BME);
	udelay (1000);

	pci_write_config_word (0, PCI_CFG_COMMAND, INITIAL_PCI_CMD);
	REG_WRITE (PCI_CSR_BASE, PCI_ISR_OFFSET, PCI_ISR_PSE
		   | PCI_ISR_PFE | PCI_ISR_PPE | PCI_ISR_AHBE);
#ifdef CONFIG_PCI_SCAN_SHOW
	printf ("Device  bus  dev  func  deviceID  vendorID \n");
#endif
	pci_bus_scan ();
}

void configure_pins (void)
{
	unsigned int regval;

	/* Disable clock on GPIO PIN 14 */
	READ_GPIO_REG (IXP425_GPIO_GPCLKR, regval);
	WRITE_GPIO_REG (IXP425_GPIO_GPCLKR, regval & (~(1 << 8)));
	READ_GPIO_REG (IXP425_GPIO_GPCLKR, regval);

	READ_GPIO_REG (IXP425_GPIO_GPOER, regval);
	WRITE_GPIO_REG (IXP425_GPIO_GPOER,
			(((~(3 << 13)) & regval) | (0xf << 8)));
	READ_GPIO_REG (IXP425_GPIO_GPOER, regval);

	READ_GPIO_REG (IXP425_GPIO_GPIT2R, regval);
	WRITE_GPIO_REG (IXP425_GPIO_GPIT2R,
			(regval &
			 ((0x1 << 9) | (0x1 << 6) | (0x1 << 3) | 0x1)));
	READ_GPIO_REG (IXP425_GPIO_GPIT2R, regval);

	READ_GPIO_REG (IXP425_GPIO_GPISR, regval);
	WRITE_GPIO_REG (IXP425_GPIO_GPISR, (regval | (0xf << 8)));
	READ_GPIO_REG (IXP425_GPIO_GPISR, regval);
}

void sys_pci_gpio_clock_config (void)
{
	unsigned int regval;

	READ_GPIO_REG (IXP425_GPIO_GPCLKR, regval);
	regval |= 0x1 << 4;
	WRITE_GPIO_REG (IXP425_GPIO_GPCLKR, regval);
	READ_GPIO_REG (IXP425_GPIO_GPCLKR, regval);
	regval |= 0x1 << 8;
	WRITE_GPIO_REG (IXP425_GPIO_GPCLKR, regval);
}

void pci_bus_scan (void)
{
	unsigned int bus = 0, dev, func = 0;
	unsigned short data16;
	unsigned int data32;
	unsigned char intPin;

	/* Assign first device to ourselves */
	devices[0].bus = 0;
	devices[0].device = 0;
	devices[0].func = 0;

	crp_read (PCI_CFG_VENDOR_ID, &data32);

	devices[0].vendor_id = data32 & IXP425_PCI_BOTTOM_WORD_OF_LONG_MASK;
	devices[0].device_id = data32 >> 16;
	devices[0].error = FALSE;
	devices[0].bar[NO_BAR].size = 0;	/*dummy - required */

	nDevices = 1;

	nMBars = 0;
	nIOBars = 0;

	for (dev = 0; dev < IXP425_PCI_MAX_DEV; dev++) {

		/*Check whether a device is present */
		if (pci_device_exists (dev) != TRUE) {

			/*Clear error bits in ISR, write 1 to clear */
			REG_WRITE (PCI_CSR_BASE, PCI_ISR_OFFSET, PCI_ISR_PSE
				   | PCI_ISR_PFE | PCI_ISR_PPE |
				   PCI_ISR_AHBE);
			continue;
		}

		/*A device is present, add an entry to the array */
		devices[nDevices].bus = bus;
		devices[nDevices].device = dev;
		devices[nDevices].func = func;

		pci_read_config_word (dev, PCI_CFG_VENDOR_ID, &data16);

		devices[nDevices].vendor_id = data16;

		pci_read_config_word (dev, PCI_CFG_DEVICE_ID, &data16);
		devices[nDevices].device_id = data16;

		/*The device is functioning correctly, set error to FALSE */
		devices[nDevices].error = FALSE;

		/*Figure out what BARs are on this device */
		sys_pci_bar_info_get (nDevices, bus, dev, func);
		/*Figure out what INTX# line the card uses */
		pci_read_config_byte (dev, PCI_CFG_DEV_INT_PIN, &intPin);

		/*assign the appropriate irq line */
		if (intPin > PCI_IRQ_LINES) {
			devices[nDevices].error = TRUE;
		} else if (intPin != 0) {
			/*This device uses an interrupt line */
			/*devices[nDevices].irq = ixp425PciIntTranslate[dev][intPin-1]; */
			devices[nDevices].irq = intPin;
		}
#ifdef CONFIG_PCI_SCAN_SHOW
		printf ("%06d    %03d %03d %04d  %08d      %08x\n", nDevices,
			devices[nDevices].vendor_id);
#endif
		nDevices++;

	}

	calc_bars (memBars, nMBars, IXP425_PCI_BAR_MEM_BASE);
	sys_pci_device_bars_write ();

	REG_WRITE (PCI_CSR_BASE, PCI_ISR_OFFSET, PCI_ISR_PSE
		   | PCI_ISR_PFE | PCI_ISR_PPE | PCI_ISR_AHBE);
}

void sys_pci_bar_info_get (unsigned int devnum,
			   unsigned int bus,
			   unsigned int dev, unsigned int func)
{
	unsigned int data32;
	unsigned int tmp;
	unsigned int size;

	pci_write_config_dword (devnum,
				PCI_CFG_BASE_ADDRESS_0, IXP425_PCI_BAR_QUERY);
	pci_read_config_dword (devnum, PCI_CFG_BASE_ADDRESS_0, &data32);

	devices[devnum].bar[0].address = (data32 & 1);

	if (data32 & 1) {
		/* IO space */
		tmp = data32 & ~0x3;
		size = ~(tmp - 1);
		devices[devnum].bar[0].size = size;

		if (nIOBars < IXP425_PCI_MAX_BAR) {
			ioBars[nIOBars++] = &devices[devnum].bar[0];
		}
	} else {
		/* Mem space */
		tmp = data32 & ~IXP425_PCI_BOTTOM_NIBBLE_OF_LONG_MASK;
		size = ~(tmp - 1);
		devices[devnum].bar[0].size = size;

		if (nMBars < IXP425_PCI_MAX_BAR) {
			memBars[nMBars++] = &devices[devnum].bar[0];
		} else {
			devices[devnum].error = TRUE;
		}

	}

	devices[devnum].bar[1].size = 0;
}

void sortBars (PciBar * Bars[], unsigned int nBars)
{
	unsigned int i, j;
	PciBar *tmp;

	if (nBars == 0) {
		return;
	}

	/* Sort biggest to smallest */
	for (i = 0; i < nBars - 1; i++) {
		for (j = i + 1; j < nBars; j++) {
			if (Bars[j]->size > Bars[i]->size) {
				/* swap them */
				tmp = Bars[i];
				Bars[i] = Bars[j];
				Bars[j] = tmp;
			}
		}
	}
}

void calc_bars (PciBar * Bars[], unsigned int nBars, unsigned int startAddr)
{
	unsigned int i;

	if (nBars == 0) {
		return;
	}

	for (i = 0; i < nBars; i++) {
		Bars[i]->address |= startAddr;
		startAddr += Bars[i]->size;
	}
}

void sys_pci_device_bars_write (void)
{
	unsigned int i;
	int addr;

	for (i = 1; i < nDevices; i++) {
		if (devices[i].error) {
			continue;
		}

		pci_write_config_dword (devices[i].device,
					PCI_CFG_BASE_ADDRESS_0,
					devices[i].bar[0].address);
		addr = BIT (31 - devices[i].device) |
			(0 << PCI_NP_AD_FUNCSL) |
			(PCI_CFG_BASE_ADDRESS_0 & ~3);
		pci_write_config_dword (devices[i].device,
					PCI_CFG_DEV_INT_LINE, devices[i].irq);

		pci_write_config_word (devices[i].device,
				       PCI_CFG_COMMAND, INITIAL_PCI_CMD);

	}
}


int pci_device_exists (unsigned int deviceNo)
{
	unsigned int vendorId;
	unsigned int regval;

	pci_read_config_dword (deviceNo, PCI_CFG_VENDOR_ID, &vendorId);

	/* There are two ways to find out an empty device.
	 *   1. check Master Abort bit after the access.
	 *   2. check whether the vendor id read back is 0x0.
	 */
	REG_READ (PCI_CSR_BASE, PCI_ISR_OFFSET, regval);
	if ((vendorId != 0x0) && ((regval & PCI_ISR_PFE) == 0)) {
		return TRUE;
	}
	/*no device present, make sure that the master abort bit is reset */

	REG_WRITE (PCI_CSR_BASE, PCI_ISR_OFFSET, PCI_ISR_PFE);
	return FALSE;
}

pci_dev_t pci_find_devices (struct pci_device_id * ids, int devNo)
{
	unsigned int i;
	unsigned int devdidvid;
	unsigned int didvid;
	unsigned int vendorId, deviceId;

	vendorId = ids->vendor;
	deviceId = ids->device;
	didvid = ((deviceId << 16) & IXP425_PCI_TOP_WORD_OF_LONG_MASK) |
		(vendorId & IXP425_PCI_BOTTOM_WORD_OF_LONG_MASK);

	for (i = devNo + 1; i < nDevices; i++) {

		pci_read_config_dword (devices[i].device, PCI_CFG_VENDOR_ID,
				       &devdidvid);

		if (devdidvid == didvid) {
			return devices[i].device;
		}
	}
	return -1;
}
#endif	/* CONFIG_PCI */