lkml.org 
[lkml]   [1998]   [Nov]   [13]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
Date
From
SubjectRe: Accessing MMIO PCI space - crossplatform (fwd)
	Ignore the last version.

Jon

---
'Cloning and the reprogramming of DNA is the first serious step in
becoming one with God.'
- Scientist G. Richard Seed

---------- Forwarded message ----------
Date: Fri, 13 Nov 1998 12:32:07 +0100 (MET)
From: Steffen Seeger <s.seeger@physik.tu-chemnitz.de>
To: ggi-develop@eskimo.com
Subject: Re: Accessing MMIO PCI space - crossplatform (fwd)

Oopss... Please ignore my other mail. This is the real one.

> This came from linux-kernel just now. We have been discussing
> some of the same issues....
>
> Jon

Please forward the following draft as a __proposal__ of a scheme that is
an easy to use (proven!!), extensible, and orthogonal solution to the
problem addressed in the mail you forwarded. [be kind, be nice,
be the good guy - don't start another KGI/not KGI war :-))]
I am not subscribed to linux-kernel, and we agreed on one 'offical'
KGI evangelist on linuux-kernel ;-)).

This is the scheme Degas/KGI is going to use. Please do also forward
any followups of this thread to my address or the list. Of course any
comments on portability/inconsistencies are welcome.

Thank you very much in advance,

Steffen

PS: This is not KGI specific at all! So if they start flaming "we don't
want graphics drivers in the kernel" point out that a) this is not
specific to graphics cards, and b) they already have drivers in there,
and now are coming to the same problems we had some time ago. This
is our (working) solution, and we offer it to improve Linux.

KINDLY!!! You know what I mean ;))

----------------- e-mail: seeger@physik.tu-chemnitz.de -----------------
------------- The GGI Project: http://www.ggi-project.org --------------


KGI/GGI System Layer Guide
Steffen Seeger (seeger@physik.tu-chemnitz.de)

I/O spaces
==========

Drivers that have to access hardware have to use I/O regions to
access the hardware. There must not be any OS specific I/O routines
used in the drivers to ensure that binary drivers are 'portable'
across platforms with the same CPU.

I/O regions are continous subsets of I/O spaces, which are the set
of all addressable locations plus some operations defined to
read and write these locations. Currently the following I/O spaces
are defined:


Identifier I/O space

pcicfg PCI configuration space
(accessed using PCI BIOS on IA32)
io ISA style I/O space
(accessed using IA32 IN/OUT instructions)
mem Memory I/O space
(accessed using IA32 MOV instructions)


Each I/O space location has four addresses associated with it:

virtual The address to be used with the operations
defined for this IO space.
io The address the device's address decoder responds to.
bus The address at which other devices on the same bus
see this device.
phys The address at which the main CPU will find this
device in physical address space.

In order to access a certain location within a I/O region, e.g. at
byte offset <offset>, using the CPU, the virtual address
<base_virt>+<offset> should be passed to the I/O operations.

In order to access the same location from another device on the same
bus, the address <base_bus>+<offset> should be passed to this
device.

In order to export this location to userspace (e.g. via memory
mapping), the mapping to be established should map the application's
virtual address <app_virt>+<offset> to the physical address
<base_phys>+<offset>.

Applications and drivers must not make any assuptions about the
mappings between the virtual, io, bus and physical addresses.
As the regions are defined to be continous, locations
with the same offset from virtual, io, bus and physical base address
will refer to the same I/O space location.

I/O region management
---------------------

In order to obtain the base addresses of a particular region,
the I/O region has to be registered with the operating system.
As long as a I/O region is registered, the mappings are guarantueed
to be constant and will not be changed by the operating system.

The functions to do region management take (except for PCICFG space,
see below) an 'struct <iospace>_region' as an argument that has the
following members:

member description

device (PCI) device this belongs to
base_virt virtual base address
base_io base address the device's address decoder responds to.
base_bus bus base address
base_phys physical base address
size size of region
decode lines used to compare address in address decoder
*name (symbolic) name of the region


Region management functions defined so far are:

check Test if a particular I/O region is registered already.
struct members assumed to be valid:
device, base_io, size, decode, name

claim Register a particular I/O region.
struct members assumed to be valid:
device, base_io, size, decode, name
If successful, all other struct members will be
valid and the I/O operations are useable.

alloc Allocate a particular region of given size, avoiding
address conflict with other (registered) regions and
claim this region.
struct members assumed to be valid:
device, size, decode, name
If successfull all other struct members will be valid.

free Unregister a previously registered region. This assumes
all members of the struct passed are valid. After freeing
a region, only the device, size, decode, name and base_io
members are valid.

The only exception to the above management procedure are PCICFG
functions used to obtain information about installed PCI devices.
The difference is, that there is a defined mapping from bus, device
and function IDs to a PCICFG virtual address. This address can
directly be used with the PCICFG I/O space operations.
In order to support PCI device autodetection, the pcicfg_find()
function is provided. The following example code will scan the whole
PCICFG space for supported cards and call do_something() for the
devices found.

__ggi_sys_u32 signatures =
{
PCICFG_SIGNATURE(PCI_VENDOR_ID_ABC, PCI_DEVICE_ID_123),
PCICFG_SIGNATURE(PCI_VENDOR_ID_DEF, PCI_DEVICE_ID_456),
...
PCICFG_SIGNATURE(0,0);
};
pcicfg_vaddr device = PCICFG_NULL;
while (! pcicfg_find(&device, signatures)) {

do_something(device);
}

For KGI drivers, policy is to have one driver (module) loaded per
installed device, which may have to be specified explicitly. Thus
it is recommended to autodetect only the first device if (and only
if) the given device specifier is invalid. The appropriate code
will then be:

... signatures definition as above
struct kgim_options_pci *pci = KGIM_OPTIONS(dpy, pci);
...
if (pci->dev == PCICFG_NULL) {

if (pcicfg_find(&pci->dev, signatures)) {

... error! no supported device found
}
}
... verify/analyze detected device



I/O region access
-----------------

For access with the main CPU, the following I/O operations are defined:

operation description

in/out data transfer device -> CPU memory/CPU memory -> device
from/to one particular I/O space location.

ins/outs 'in/out string': repeated transfer device -> CPU memory
buffer/CPU memory buffer -> device from/to one
particular I/O space location
C/C++ equivalent:
ins: while (i < cnt) { buf[i++] = *io_addr; }
outs: while (i < cnt) { *io_addr = buf[i++]; }

incu/outcu 'in/out copy upward': repeated transfer from device
to CPU/CPU to device from/to ascending I/O space
locations to ascending CPU memory locations.
C/C++ equivalent:
incu: while (i < cnt) { buf[i] = io_addr[i]; i++; }
outco: while (i < cnt) { io_addr[i] = buf[i]; i++; }

incd/outcd 'in/out copy downward': repeated transfer from device
to CPU/CPU to device from/to descending I/O space
locations to descending CPU memory locations.
incd: i = cnt; while (i--) { buf[i] = io_addr[i]; }
outcd: i = cnt; while (i--) { io_addr[i] = buf[i]; }

All these operations are defined to (1) preserve read/write order,
(2) preserve byte order (no endian swap) and (3) are assumed to
be completed before the caller resumes execution. Each operation may
be available for different access widths (8,16,32,64 bit) indicated
by a suffix.

Any of the operations may be missing, either for a particular width
only or for a whole operation. (E.g. incd/outcd, ins/outs don't make
sense with PCICFG I/O space). Also, particular I/O spaces may not be
available on some architectures (e.g. on early PC's there might not be
a PCICFG space).

=== io.h ======================================================================
/* ----------------------------------------------------------------------------
** hardware I/O layer definiton
** ----------------------------------------------------------------------------
**
** Copyright (C) 1997-1998 by Steffen Seeger
**
** ----------------------------------------------------------------------------
** MAINTAINER Steffen_Seeger
**
** $Log: io.h,v $
** Revision 1.2 1998/08/14 20:05:16 seeger_s
** - redefined IRQ interface.
**
** Revision 1.1 1998/07/19 22:54:17 seeger_s
** - implemented I/O functions
**
*/
#ifndef __ggi_io_h
#define __ggi_io_h

/* Explanations on I/O regions can be found in the system layer
** documentation.
*/

/* ----------------------------------------------------------------------------
** PCI configuration space
** ----------------------------------------------------------------------------
** These functions should be used to access the PCI configuration space.
** As the address is given by the hardware wiring, a registration
** scheme doesn't make sense. But I/O is pretty useful :) For the same
** reason, and as it is used for configuration (bootstrap) purposes,
** a physical->virtual mapping is not possible. All addresses are
** virtual and no mapping takes place.
*/
#ifdef __GGI_SYS_IO_HAS_PCICFG

#define PCICFG_NULL ((pcicfg_vaddr) -1) /* an invalid virtual address */

typedef __ggi_sys_u32 pcicfg_vaddr; /* the virtual address type */

#define PCICFG_VADDR(bus, slot, fn) \
((pcicfg_vaddr)((((bus) & 0xFF) << 24) | (PCI_DEVFN(slot,fn) << 16)))
#define PCICFG_BUS(vaddr) (((vaddr) >> 24) & 0xFF)
#define PCICFG_DEV(vaddr) PCI_SLOT(((vaddr) >> 16) & 0xFF)
#define PCICFG_FN(vaddr) PCI_FUNC(((vaddr) >> 16) & 0xFF)
#define PCICFG_REG(vaddr) ((vaddr) & 0xFFFF)
#define PCICFG_SIGNATURE(vendor, device) ((vendor << 16) | device)

extern int pcicfg_find(pcicfg_vaddr *addr, const __ggi_sys_u32 *signatures);

extern __ggi_sys_u8 pcicfg_in8 (const pcicfg_vaddr vaddr);
extern __ggi_sys_u16 pcicfg_in16(const pcicfg_vaddr vaddr);
extern __ggi_sys_u32 pcicfg_in32(const pcicfg_vaddr vaddr);

extern void pcicfg_out8 (const __ggi_sys_u8 val, const pcicfg_vaddr vaddr);
extern void pcicfg_out16(const __ggi_sys_u16 val, const pcicfg_vaddr vaddr);
extern void pcicfg_out32(const __ggi_sys_u32 val, const pcicfg_vaddr vaddr);

#endif /* #ifdef __GGI_SYS_IO_HAS_PCICFG */


/* ----------------------------------------------------------------------------
** Intel style in/out I/O space
** ----------------------------------------------------------------------------
** The io_in.. / io_out.. functions generate the neccesary hardware
** actions to do a read/write cycle in a cards I/O space. (Registers
** accessed via the in.. / out.. instructions on i386 architecture.)
*/
#ifdef __GGI_SYS_IO_HAS_IO

#define IO_NULL ((io_vaddr) 0) /* an invalid virtual address */
#define IO_DECODE_ALL ((io_addr) -1) /* all lines being decoded */

typedef unsigned int io_addr; /* the physical address type */
typedef unsigned int io_vaddr; /* the virtual address type */

struct io_region
{
pcicfg_vaddr device; /* (PCI) device this belongs to */
io_vaddr base_virt; /* virtual base address */
io_addr base_io; /* I/O base address */
io_addr base_bus; /* bus address */
io_addr base_phys; /* physical address */
io_addr size; /* size of region */
io_addr decode; /* decoded I/O address lines */
char *name; /* name of the region */
};

extern int io_check_region(struct io_region *r);
extern io_vaddr io_claim_region(struct io_region *r);
extern io_vaddr io_free_region(struct io_region *r);
extern io_vaddr io_alloc_region(struct io_region *r);

extern __ggi_sys_u8 io_in8 (const io_vaddr vaddr);
extern __ggi_sys_u16 io_in16(const io_vaddr vaddr);
extern __ggi_sys_u32 io_in32(const io_vaddr vaddr);

extern void io_out8 (const __ggi_sys_u8 val, const io_vaddr vaddr);
extern void io_out16(const __ggi_sys_u16 val, const io_vaddr vaddr);
extern void io_out32(const __ggi_sys_u32 val, const io_vaddr vaddr);

extern void io_ins8 (const io_vaddr addr, void *buf, unsigned long cnt);
extern void io_ins16(const io_vaddr addr, void *buf, unsigned long cnt);
extern void io_ins32(const io_vaddr addr, void *buf, unsigned long cnt);

extern void io_outs8 (const io_vaddr addr, const void *buf, unsigned long cnt);
extern void io_outs16(const io_vaddr addr, const void *buf, unsigned long cnt);
extern void io_outs32(const io_vaddr addr, const void *buf, unsigned long cnt);

#endif /* #ifdef __GGI_SYS_IO_HAS_IO */

/* ----------------------------------------------------------------------------
** memory I/O space
** ----------------------------------------------------------------------------
** This is the 'normal' shared memory I/O space accessed using the
** mov instructions on i386 architecture. The difference between
** *vaddr = val and mem_out32(val, vaddr) is that the latter will not
** be optimized away when compiling with maximum optimization.
*/
#ifdef __GGI_SYS_IO_HAS_MEM

#define MEM_NULL ((mem_vaddr) 0) /* an invalid virtual address */
#define MEM_DECODE_ALL ((mem_addr) -1) /* all lines being decoded */

typedef unsigned int mem_addr; /* the physical address type */
typedef void * mem_vaddr; /* the virtual address type */

struct mem_region
{
pcicfg_vaddr device; /* (PCI) device this belongs to */
mem_vaddr base_virt; /* virtual base address */
mem_addr base_io; /* the I/O base address */
mem_addr base_bus; /* bus address */
mem_addr base_phys; /* physical address */
mem_addr size; /* size of region */
mem_addr decode; /* decoded io address lines */
char *name; /* name of the region */
};

extern int mem_check_region(struct mem_region *r);
extern mem_vaddr mem_claim_region(struct mem_region *r);
extern mem_vaddr mem_free_region(struct mem_region *r);
extern mem_vaddr mem_alloc_region(struct mem_region *r);

extern __ggi_sys_u8 mem_in8 (const mem_vaddr vaddr);
extern __ggi_sys_u16 mem_in16(const mem_vaddr vaddr);
extern __ggi_sys_u32 mem_in32(const mem_vaddr vaddr);

extern void mem_out8 (const __ggi_sys_u8 val, const mem_vaddr vaddr);
extern void mem_out16(const __ggi_sys_u16 val, const mem_vaddr vaddr);
extern void mem_out32(const __ggi_sys_u32 val, const mem_vaddr vaddr);

extern void mem_ins8 (const mem_vaddr addr, void *buf, unsigned long cnt);
extern void mem_ins16(const mem_vaddr addr, void *buf, unsigned long cnt);
extern void mem_ins32(const mem_vaddr addr, void *buf, unsigned long cnt);

extern void mem_outs8 (const mem_vaddr addr, const void *buf,unsigned long cnt);
extern void mem_outs16(const mem_vaddr addr, const void *buf,unsigned long cnt);
extern void mem_outs32(const mem_vaddr addr, const void *buf,unsigned long cnt);

#endif /* #ifdef GGI_SYS_IO_HAS_MEM */

/* ----------------------------------------------------------------------------
** irq handling
** ----------------------------------------------------------------------------
** use similar to io regions.
*/
#ifdef __GGI_SYS_IO_HAS_IRQ

#define IRQ_NULL ((unsigned int) -1) /* an invalid irq line */
#define IRQ_DECODE_ALL ((irq_mask) -1) /* all lines being decoded */

typedef unsigned int irq_mask;

enum irq_flags
{
IF_SHARED_IRQ = 0x00000001
};

struct irq_line
{
enum irq_flags flags; /* properties */
char *name; /* name of the line */
unsigned int line; /* requested IRQ line */

void *high_priv; /* high priority handler priv. */
int (*High)(void *priv, void *regs); /* high priority handler */

void *low_priv; /* low priority handler private */
void (*Low)(void *priv); /* low priority handler, improve!!! */
};

/* These may be OS dependent, and have to be supplied by the OS/kernel
** layer. As they aren't performance critical either, a call doesn't hurt.
*/
extern int irq_claim_line(struct irq_line *irq);
extern void irq_free_line(struct irq_line *irq);

#endif /* #ifdef __GGI_SYS_IO_HAS_IRQ */

#undef __GGI_SYS_IO_HAS_PCICFG
#undef __GGI_SYS_IO_HAS_IO
#undef __GGI_SYS_IO_HAS_MEM
#undef __GGI_SYS_IO_HAS_IRQ

#endif /* #ifdef __ggi_io_h */
=== io.h ======================================================================



-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/

\
 
 \ /
  Last update: 2005-03-22 13:45    [W:0.054 / U:0.864 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site