summaryrefslogtreecommitdiffstats
path: root/arch/arm/kernel/hw-ebsa285.c
blob: e3385696b076c1e640b86985289425466ed209ed (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
/*
 * arch/arm/kernel/hw-ebsa286.c
 *
 * EBSA285 hardware specific functions
 *
 * Copyright (C) 1998 Russell King, Phil Blundel
 */
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/ptrace.h>
#include <linux/interrupt.h>
#include <linux/init.h>

#include <asm/irq.h>
#include <asm/system.h>

extern int setup_arm_irq(int, struct irqaction *);

extern void pci_set_cmd(struct pci_dev *dev, unsigned short clear, unsigned short set);
extern void pci_set_base_addr(struct pci_dev *dev, int idx, unsigned int addr);
extern void pci_set_irq_line(struct pci_dev *dev, unsigned int irq);

static int irqmap_ebsa[] __initdata = { 9, 8, 18, 11 };
static int irqmap_cats[] __initdata = { 18, 8, 9, 11 };

__initfunc(static int ebsa_irqval(struct pci_dev *dev))
{
	unsigned char pin;
	
	pcibios_read_config_byte(dev->bus->number,
				 dev->devfn,
				 PCI_INTERRUPT_PIN,
				 &pin);
	
	return irqmap_ebsa[(PCI_SLOT(dev->devfn) + pin) & 3];
}

__initfunc(static int cats_irqval(struct pci_dev *dev))
{
	if (dev->irq >= 128)
		return 32 + (dev->irq & 0x1f);

	switch (dev->irq) {
	case 1:
	case 2:
	case 3:
	case 4:
		return irqmap_cats[dev->irq - 1];
	case 0:
		return 0;
	}

	printk("PCI: device %02x:%02x has unknown irq line %x\n",
	       dev->bus->number, dev->devfn, dev->irq);
	return 0;
}

__initfunc(void pcibios_fixup_ebsa285(struct pci_dev *dev))
{
	char cmd;

	/* sort out the irq mapping for this device */
	switch (machine_type) {
	case MACH_TYPE_EBSA285:
		dev->irq = ebsa_irqval(dev);
		break;
	case MACH_TYPE_CATS:
		dev->irq = cats_irqval(dev);
		break;
	}

	/* Turn on bus mastering - boot loader doesn't
	 * - perhaps it should! - dag
	 */
	pci_read_config_byte(dev, PCI_COMMAND, &cmd);
	pci_write_config_byte(dev, PCI_COMMAND, cmd | PCI_COMMAND_MASTER);
}

static void irq_pci_err(int irq, void *dev_id, struct pt_regs *regs)
{
	const char *err = "unknown";
	unsigned long cmd = *(unsigned long *)0xfe000004 & 0xffff;
	unsigned long ctrl = *(unsigned long *)0xfe00013c & 0xffffde07;
	static unsigned long next_warn[7];
	int idx = 6;

	switch(irq) {
	case IRQ_PCIPARITY:
		*(unsigned long *)0xfe000004 = cmd | 1 << 31;
		idx = 0;
		err = "parity";
		break;

	case IRQ_PCITARGETABORT:
		*(unsigned long *)0xfe000004 = cmd | 1 << 28;
		idx = 1;
		err = "target abort";
		break;

	case IRQ_PCIMASTERABORT:
		*(unsigned long *)0xfe000004 = cmd | 1 << 29;
		idx = 2;
		err = "master abort";
		break;

	case IRQ_PCIDATAPARITY:
		*(unsigned long *)0xfe000004 = cmd | 1 << 24;
		idx = 3;
		err = "data parity";
		break;

	case IRQ_DISCARDTIMER:
		*(unsigned long *)0xfe00013c = ctrl | 1 << 8;
		idx = 4;
		err = "discard timer";
		break;

	case IRQ_SERR:
		*(unsigned long *)0xfe00013c = ctrl | 1 << 3;
		idx = 5;
		err = "system";
		break;
	}
	if (time_after_eq(jiffies, next_warn[idx])) {
		next_warn[idx] = jiffies + 3 * HZ / 100;
		printk(KERN_ERR "PCI %s error detected\n", err);
	}
}

static struct irqaction irq_pci_error = {
	irq_pci_err, SA_INTERRUPT, 0, "PCI error", NULL, NULL
};

__initfunc(void pcibios_init_ebsa285(void))
{
	setup_arm_irq(IRQ_PCIPARITY, &irq_pci_error);
	setup_arm_irq(IRQ_PCITARGETABORT, &irq_pci_error);
	setup_arm_irq(IRQ_PCIMASTERABORT, &irq_pci_error);
	setup_arm_irq(IRQ_PCIDATAPARITY, &irq_pci_error);
	setup_arm_irq(IRQ_DISCARDTIMER, &irq_pci_error);
	setup_arm_irq(IRQ_SERR, &irq_pci_error);

	/*
	 * Map our SDRAM at a known address in PCI space, just in case
	 * the firmware had other ideas.  Using a nonzero base is slightly
	 * bizarre but apparently necessary to avoid problems with some
	 * video cards.
	 *
	 * We should really only do this if the central function is enabled.
	 */
	*(unsigned long *)0xfe000010 = 0;
	*(unsigned long *)0xfe000018 = 0xe0000000;
	*(unsigned long *)0xfe0000f8 = 0;
	*(unsigned long *)0xfe0000fc = 0;
	*(unsigned long *)0xfe000100 = 0x01fc0000;
	*(unsigned long *)0xfe000104 = 0;
	*(unsigned long *)0xfe000108 = 0x80000000;
	*(unsigned long *)0xfe000004 = 0x17;
}