summaryrefslogtreecommitdiff
path: root/src/arch/ppc/lib/pci_ops.c
blob: 69f34e8fc9238c733c54bad53650eba8bc3cff9d (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
#include <console/console.h>
#include <arch/io.h>
#include <arch/pciconf.h>
#include <device/pci.h>
#include <device/pci_ids.h>
#include <device/pci_ops.h>

static const struct pci_ops *conf;
struct pci_ops {
	uint8_t (*read8) (uint8_t bus, int devfn, int where);
	uint16_t (*read16) (uint8_t bus, int devfn, int where);
	uint32_t (*read32) (uint8_t bus, int devfn, int where);
	int (*write8) (uint8_t bus, int devfn, int where, uint8_t val);
	int (*write16) (uint8_t bus, int devfn, int where, uint16_t val);
	int (*write32) (uint8_t bus, int devfn, int where, uint32_t val);
};

struct pci_ops pci_direct_ppc;

#define CONFIG_CMD(bus,devfn,where) \
		((bus << 16) | (devfn << 8) | (where & ~3) | 0x80000000)

/*
 * Direct access to PCI hardware...
 */

/*
 * Before we decide to use direct hardware access mechanisms, we try to do some
 * trivial checks to ensure it at least _seems_ to be working -- we just test
 * whether bus 00 contains a host bridge (this is similar to checking
 * techniques used in XFree86, but ours should be more reliable since we
 * attempt to make use of direct access hints provided by the PCI BIOS).
 *
 * This should be close to trivial, but it isn't, because there are buggy
 * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID.
 */
static int pci_sanity_check(const struct pci_ops *o)
{
	uint16_t class, vendor;
	uint8_t bus;
	int devfn;
#define PCI_CLASS_BRIDGE_HOST           0x0600
#define PCI_CLASS_DISPLAY_VGA           0x0300
#define PCI_VENDOR_ID_COMPAQ            0x0e11
#define PCI_VENDOR_ID_INTEL             0x8086
#define PCI_VENDOR_ID_MOTOROLA          0x1057
#define PCI_VENDOR_ID_IBM	        0x1014

	for (bus = 0, devfn = 0; devfn < 0x100; devfn++) {
		class = o->read16(bus, devfn, PCI_CLASS_DEVICE);
		vendor = o->read16(bus, devfn, PCI_VENDOR_ID);
		if (((class == PCI_CLASS_BRIDGE_HOST) || 
		     (class == PCI_CLASS_DISPLAY_VGA)) ||
			((vendor == PCI_VENDOR_ID_INTEL) || 
			(vendor == PCI_VENDOR_ID_COMPAQ) ||
			(vendor == PCI_VENDOR_ID_MOTOROLA) ||
			(vendor == PCI_VENDOR_ID_IBM))) {
			return 1;
		}
	}

	printk_err("PCI: Sanity check failed\n");
	return 0;
}

uint8_t pci_read_config8(device_t dev, unsigned where)
{
	return conf->read8(dev->bus->secondary, dev->path.u.pci.devfn, where);
}

uint16_t pci_read_config16(struct device *dev, unsigned where)
{
	return conf->read16(dev->bus->secondary, dev->path.u.pci.devfn, where);
}

uint32_t pci_read_config32(struct device *dev, unsigned where)
{
	return conf->read32(dev->bus->secondary, dev->path.u.pci.devfn, where);
}

void pci_write_config8(struct device *dev, unsigned where, uint8_t val)
{
	conf->write8(dev->bus->secondary, dev->path.u.pci.devfn, where, val);
}

void pci_write_config16(struct device *dev, unsigned where, uint16_t val)
{
	conf->write16(dev->bus->secondary, dev->path.u.pci.devfn, where, val);
}

void pci_write_config32(struct device *dev, unsigned where, uint32_t val)
{
	conf->write32(dev->bus->secondary, dev->path.u.pci.devfn, where, val);
}

/** Set the method to be used for PCI
 */
void pci_set_method(void)
{
	conf = &pci_direct_ppc;
	pci_sanity_check(conf);
}

static uint8_t pci_ppc_read_config8(unsigned char bus, int devfn, int where)
{
	uint8_t res;

	out_le32((unsigned *)PCIC0_CFGADDR, CONFIG_CMD(bus, devfn, where));
	res = in_8((unsigned char *)PCIC0_CFGDATA + (where & 3));
	printk_spew("read8(0x%x,0x%x,0x%x)=0x%x\n", bus, devfn, where, res);
	return res;
}

static uint16_t pci_ppc_read_config16(unsigned char bus, int devfn, int where)
{
	uint16_t res;

	out_le32((unsigned *)PCIC0_CFGADDR, CONFIG_CMD(bus, devfn, where));
	res = in_le16((unsigned short *)PCIC0_CFGDATA + (where & 2));
	printk_spew("read16(0x%x,0x%x,0x%x)=0x%x\n", bus, devfn, where, res);
	return res;
}

static uint32_t pci_ppc_read_config32(unsigned char bus, int devfn, int where)
{
	uint32_t res;

	out_le32((unsigned *)PCIC0_CFGADDR, CONFIG_CMD(bus, devfn, where));
	res = in_le32((unsigned *)PCIC0_CFGDATA);
	printk_spew("read32(0x%x,0x%x,0x%x)=0x%x\n", bus, devfn, where, res);
	return res;
}

static int pci_ppc_write_config8(unsigned char bus, int devfn, int where, uint8_t data)
{
	out_le32((unsigned *)PCIC0_CFGADDR, CONFIG_CMD(bus, devfn, where));
	out_8((unsigned char *)PCIC0_CFGDATA + (where & 3), data);
	printk_spew("write8(0x%x,0x%x,0x%x)<-0x%x\n", bus, devfn, where, data);
	return 0;
}

static int pci_ppc_write_config16(unsigned char bus, int devfn, int where, uint16_t data)
{
	out_le32((unsigned *)PCIC0_CFGADDR, CONFIG_CMD(bus, devfn, where));
	out_le16((unsigned short *)PCIC0_CFGDATA + (where & 2), data);
	printk_spew("write16(0x%x,0x%x,0x%x)<-0x%x\n", bus, devfn, where, data);
	return 0;
}

static int pci_ppc_write_config32(unsigned char bus, int devfn, int where, uint32_t data)
{
	out_le32((unsigned *)PCIC0_CFGADDR, CONFIG_CMD(bus, devfn, where));
	out_le32((unsigned *)PCIC0_CFGDATA, data);
	printk_spew("write32(0x%x,0x%x,0x%x)<-0x%x\n", bus, devfn, where, data);
	return 0;
}

struct pci_ops pci_direct_ppc =
{
    pci_ppc_read_config8,
    pci_ppc_read_config16,
    pci_ppc_read_config32,
    pci_ppc_write_config8,
    pci_ppc_write_config16,
    pci_ppc_write_config32     
};