summaryrefslogtreecommitdiff
path: root/hw/xfree86/os-support/bus/axpPci.c
blob: c59c06804dc2717252ea2d3bf6fb54b5d69ff618 (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
/*
 * Copyright 1998 by Concurrent Computer Corporation
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and that
 * both that copyright notice and this permission notice appear in
 * supporting documentation, and that the name of Concurrent Computer
 * Corporation not be used in advertising or publicity pertaining to
 * distribution of the software without specific, written prior
 * permission.  Concurrent Computer Corporation makes no representations
 * about the suitability of this software for any purpose.  It is
 * provided "as is" without express or implied warranty.
 *
 * CONCURRENT COMPUTER CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD
 * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS, IN NO EVENT SHALL CONCURRENT COMPUTER CORPORATION BE
 * LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
 * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 *
 * Copyright 1998 by Metro Link Incorporated
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and that
 * both that copyright notice and this permission notice appear in
 * supporting documentation, and that the name of Metro Link
 * Incorporated not be used in advertising or publicity pertaining to
 * distribution of the software without specific, written prior
 * permission.  Metro Link Incorporated makes no representations
 * about the suitability of this software for any purpose.  It is
 * provided "as is" without express or implied warranty.
 *
 * METRO LINK INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD
 * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS, IN NO EVENT SHALL METRO LINK INCORPORATED BE
 * LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
 * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */

#ifdef HAVE_XORG_CONFIG_H
#include <xorg-config.h>
#endif

#include <stdio.h>
#include "compiler.h"
#include "xf86.h"
#include "xf86Priv.h"
#include "xf86_OSlib.h"
#include "Pci.h"

#include <asm/unistd.h>
#include "../linux/lnx.h"	/* for _iobase */

/*
 * Alpha/Linux platform specific PCI access functions
 */
static CARD32 axpPciCfgRead(PCITAG tag, int off);
static void axpPciCfgWrite(PCITAG, int off, CARD32 val);
static void axpPciCfgSetBits(PCITAG tag, int off, CARD32 mask, CARD32 bits);

static pciBusFuncs_t axpFuncs0 = {
/* pciReadLong      */	axpPciCfgRead,
/* pciWriteLong     */	axpPciCfgWrite,
/* pciSetBitsLong   */	axpPciCfgSetBits,
/* pciAddrHostToBus */	pciAddrNOOP,
/* pciAddrBusToHost */	pciAddrNOOP
};

typedef struct _axpDomainRec {
    int domain, hose;
    int root_bus;
    unsigned long dense_io,  sparse_io;
    unsigned long dense_mem, sparse_mem;
    IOADDRESS mapped_io;
} axpDomainRec, *axpDomainPtr;

#define MAX_DOMAINS (MAX_PCI_BUSES / 256)
static axpDomainPtr xf86DomainInfo[MAX_DOMAINS] = { NULL, };
static int	    pciNumDomains = 0;

/*
 * For debug, domain assignment can start downward from a fixed base 
 * (instead of up from 0) by defining FORCE_HIGH_DOMAINS. This allows
 * debug of large domain numbers and sparse domain numbering on systems
 * which don't have as many hoses.
 */
#if 0
# define FORCE_HIGH_DOMAINS MAX_DOMAINS /* assign domains downward from here */
#endif

/*
 * If FORCE_HIGH_DOMAINS is set, make sure it's not larger than the
 * max domain
 */
#if defined(FORCE_HIGH_DOMAINS) && (FORCE_HIGH_DOMAINS > MAX_DOMAINS)
# undef FORCE_HIGH_DOMAINS
# define FORCE_HIGH_DOMAINS MAX_DOMAINS
#endif

static int
axpSetupDomains(void)
{
    axpDomainRec axpDomain;
    int numDomains = 0;
    int hose;

#ifndef INCLUDE_XF86_NO_DOMAIN

#ifdef FORCE_HIGH_DOMAINS
    xf86Msg(X_WARNING, 
	    "DEBUG OPTION FORCE_HIGH_DOMAINS in use - DRI will *NOT* work\n");
    numDomains = FORCE_HIGH_DOMAINS;
#endif

    /*
     * Since each hose has a different address space, hoses are a perfect
     * overlay for domains, so set up one domain for each hose present
     * in the system. We have to loop through all possible hoses because
     * some systems allow sparse I/O controllers.
     */
    for(hose = 0; hose < MAX_DOMAINS; hose++) {
	axpDomain.root_bus = _iobase(IOBASE_ROOT_BUS, hose, -1, -1);
	if (axpDomain.root_bus < 0) continue;

	axpDomain.hose = hose;

#ifndef FORCE_HIGH_DOMAINS

	axpDomain.domain = axpDomain.hose = hose;
	numDomains = axpDomain.domain + 1;

#else /* FORCE_HIGH_DOMAINS */

	axpDomain.domain = numDomains - hose - 1;

	xf86Msg(X_WARNING, 
		"FORCE_HIGH_DOMAINS - assigned hose %d to domain %d\n",
		axpDomain.hose, axpDomain.domain);

#endif /* FORCE_HIGH_DOMAINS */

	axpDomain.dense_io   = _iobase(IOBASE_DENSE_IO,   hose, -1, -1);
	axpDomain.sparse_io  = _iobase(IOBASE_SPARSE_IO,  hose, -1, -1);
        axpDomain.mapped_io  = 0;
	axpDomain.dense_mem  = _iobase(IOBASE_DENSE_MEM,  hose, -1, -1);
	axpDomain.sparse_mem = _iobase(IOBASE_SPARSE_MEM, hose, -1, -1);

	xf86DomainInfo[axpDomain.domain] = xnfalloc(sizeof(axpDomainRec));
	*(xf86DomainInfo[axpDomain.domain]) = axpDomain;

	/*
	 * For now, only allow a single domain (hose) on sparse i/o systems.
	 *
	 * Allowing multiple domains on sparse systems would require:
	 *	1) either
	 *		a) revamping the sparse video mapping code to allow 
	 *		   for multiple unrelated address regions
	 *		  	-- OR -- 
	 *		b) implementing sparse mapping directly in 
	 *		   xf86MapDomainMemory
	 *	2) revaming read/write sparse routines to correctly handle
	 *	   the solution to 1)
	 *	3) implementing a sparse I/O system (mapping, inX/outX)
	 *	   independent of glibc, since the glibc version only
	 *	   supports hose 0
	 */
	if (axpDomain.sparse_io) {
	    if (_iobase(IOBASE_ROOT_BUS, hose + 1, -1, -1) >= 0) {
		/*
		 * It's a sparse i/o system with (at least) one more hose,
		 * show a message indicating that video is constrained to 
		 * hose 0
		 */
		xf86Msg(X_INFO, 
			"Sparse I/O system - constraining video to hose 0\n");
	    }
	    break;
	}
    }

#else /* INCLUDE_XF86_NO_DOMAIN */

    /*
     * domain support is not included, so just set up a single domain (0)
     * to represent the first hose so that axpPciInit will still have
     * be able to set up the root bus
     */
    xf86DomainInfo[0] = xnfalloc(sizeof(axpDomainRec));
    *(xf86DomainInfo[0]) = axpDomain;
    numDomains = 1;

#endif /* INCLUDE_XF86_NO_DOMAIN */

    return numDomains;
}

void  
axpPciInit()
{
    axpDomainPtr pDomain;
    int domain, bus;

    pciNumDomains = axpSetupDomains();

    for(domain = 0; domain < pciNumDomains; domain++) {
	if (!(pDomain = xf86DomainInfo[domain])) continue;

	/*
	 * Since any bridged buses will be behind a probed pci-pci bridge, 
	 * only set up the root bus for each domain (hose) and the bridged 
	 * buses will be set up as they are found.
	 */
	/* make a bus with both the domain and the root bus in it */
	bus = PCI_MAKE_BUS(domain, pDomain->root_bus);
	pciBusInfo[bus] = xnfalloc(sizeof(pciBusInfo_t));
	(void)memset(pciBusInfo[bus], 0, sizeof(pciBusInfo_t));

	pciBusInfo[bus]->configMech = PCI_CFG_MECH_OTHER;
	pciBusInfo[bus]->numDevices = 32;
	pciBusInfo[bus]->funcs = &axpFuncs0;
	pciBusInfo[bus]->pciBusPriv = pDomain;

	pciNumBuses = bus + 1;
    }
}

/*
 * Alpha/Linux PCI configuration space access routines
 */
static int 
axpPciBusFromTag(PCITAG tag)
{
    pciBusInfo_t *pBusInfo;
    axpDomainPtr pDomain;
    int bus, dfn;

    bus = PCI_BUS_FROM_TAG(tag);
    if ((bus >= pciNumBuses) 
	|| !(pBusInfo = pciBusInfo[bus])
	|| !(pDomain = pBusInfo->pciBusPriv)
	|| (pDomain->domain != PCI_DOM_FROM_TAG(tag))) return -1;

    bus = PCI_BUS_NO_DOMAIN(bus); /* should just be root_bus */
    dfn = PCI_DFN_FROM_TAG(tag);
    if (_iobase(IOBASE_HOSE, -1, bus, dfn) != pDomain->hose) return -1;

    return bus;
}

static CARD32
axpPciCfgRead(PCITAG tag, int off)
{
    int bus, dfn;
    CARD32 val = 0xffffffff;

    if ((bus = axpPciBusFromTag(tag)) >= 0) {
	dfn = PCI_DFN_FROM_TAG(tag);

	syscall(__NR_pciconfig_read, bus, dfn, off, 4, &val);
    }
    return(val);	
}

static void
axpPciCfgWrite(PCITAG tag, int off, CARD32 val)
{
    int bus, dfn;

    if ((bus = axpPciBusFromTag(tag)) >= 0) {
	dfn = PCI_DFN_FROM_TAG(tag);
	syscall(__NR_pciconfig_write, bus, dfn, off, 4, &val);
    }
}

static void
axpPciCfgSetBits(PCITAG tag, int off, CARD32 mask, CARD32 bits)
{
    int bus, dfn;
    CARD32 val = 0xffffffff;

    if ((bus = axpPciBusFromTag(tag)) >= 0) {
	dfn = PCI_DFN_FROM_TAG(tag);

	syscall(__NR_pciconfig_read, bus, dfn, off, 4, &val);
	val = (val & ~mask) | (bits & mask);
	syscall(__NR_pciconfig_write, bus, dfn, off, 4, &val);
    }
}

#ifndef INCLUDE_XF86_NO_DOMAIN

/*
 * Alpha/Linux addressing domain support
 */

_X_EXPORT int
xf86GetPciDomain(PCITAG Tag)
{
    return PCI_DOM_FROM_TAG(Tag);
}

_X_EXPORT pointer
xf86MapDomainMemory(int ScreenNum, int Flags, PCITAG Tag,
		    ADDRESS Base, unsigned long Size)
{
    axpDomainPtr pDomain;
    int domain = PCI_DOM_FROM_TAG(Tag);

    if ((domain < 0) || (domain >= pciNumDomains) ||
	!(pDomain = xf86DomainInfo[domain])) 
	FatalError("%s called with invalid parameters\n", __FUNCTION__);

    /*
     * xf86MapVidMem already does what we need, but remember to subtract
     * _bus_base() (the physical dense memory root of hose 0) since 
     * xf86MapVidMem is expecting an offset relative to _bus_base() rather
     * than an actual physical address.
     */
    return xf86MapVidMem(ScreenNum, Flags, 
			 pDomain->dense_mem + Base - _bus_base(), Size);
}

IOADDRESS
xf86MapLegacyIO(struct pci_device *dev)
{
    axpDomainPtr pDomain;
    const int domain = dev->domain;

    if ((domain < 0) || (domain >= pciNumDomains) ||
	!(pDomain = xf86DomainInfo[domain])) 
	FatalError("%s called with invalid parameters\n", __FUNCTION__);

    /*
     * Use glibc inx/outx routines for sparse I/O, so just return the
     * base [this is ok since we also constrain sparse I/O systems to
     * a single domain in axpSetupDomains()]
     */
    if (pDomain->sparse_io) return 0;

    /*
     * I/O addresses on Alpha are really just different physical memory
     * addresses that the system corelogic turns into I/O commands on the
     * bus, so just use xf86MapVidMem to map I/O as well, but remember
     * to subtract _bus_base() (the physical dense memory root of hose 0)
     * since xf86MapVidMem is expecting an offset relative to _bus_base()
     * rather than an actual physical address.
     *
     * Map the entire I/O space (64kB) at once and only once.
     */
    if (!pDomain->mapped_io)
        pDomain->mapped_io = (IOADDRESS)xf86MapVidMem(-1, VIDMEM_MMIO,
		   	            pDomain->dense_io - _bus_base(), 
                                    0x10000);

    return pDomain->mapped_io;
}

resPtr
xf86AccResFromOS(resPtr pRes)
{
    resRange range;
    int domain;

    for(domain = 0; domain < pciNumDomains; domain++) {
	if (!xf86DomainInfo[domain]) continue;

	/*
	 * Fallback is to claim the following areas:
	 *
	 * 0x000c0000 - 0x000effff  location of VGA and other extensions ROMS
	 */

	RANGE(range, 0x000c0000, 0x000effff, 
	      RANGE_TYPE(ResExcMemBlock, domain));
	pRes = xf86AddResToList(pRes, &range, -1);

	/*
	 * Fallback would be to claim well known ports in the 0x0 - 0x3ff 
	 * range along with their sparse I/O aliases, but that's too 
	 * imprecise.  Instead claim a bare minimum here.
	 */
	RANGE(range, 0x00000000, 0x000000ff, 
	      RANGE_TYPE(ResExcIoBlock, domain)); /* For mainboard */
	pRes = xf86AddResToList(pRes, &range, -1);

	/*
	 * At minimum, the top and bottom resources must be claimed, so that
	 * resources that are (or appear to be) unallocated can be relocated.
	 */
	RANGE(range, 0x00000000, 0x00000000, 
	      RANGE_TYPE(ResExcMemBlock, domain));
	pRes = xf86AddResToList(pRes, &range, -1);
	RANGE(range, 0xffffffff, 0xffffffff, 
	      RANGE_TYPE(ResExcMemBlock, domain));
	pRes = xf86AddResToList(pRes, &range, -1);
/*  	RANGE(range, 0x00000000, 0x00000000, 
	      RANGE_TYPE(ResExcIoBlock, domain));
        pRes = xf86AddResToList(pRes, &range, -1); */
	RANGE(range, 0xffffffff, 0xffffffff, 
	      RANGE_TYPE(ResExcIoBlock, domain));
	pRes = xf86AddResToList(pRes, &range, -1);
    }

    return pRes;
}

#endif /* !INCLUDE_XF86_NO_DOMAIN */