chapter 8 hardware management how to access i/o ports and i/o memory and remain portable across different linux platforms? use stand pc parallel port as example, and frame-buffer video memory to show memory-mapped i/o i/o porta and i/o memory device has set of registers. we read and write them. usually they are consecutive. x86 has different address space for hardware from main memory itself. and special CPU instructions for this 64k space. other CPUs (e.g., 68k of Motorola ...) have registers in memory-mapped space. access with pointers. linux assumes x86 pov, and hides port instructions to memory-mapped space however there are also bus differences. ISA fits this model. PCI devices map registers into a memory address region. I/O registers and conventional memory beware compiler optimizations... i/o ops have side effects. memory locations have none. *register = 0x01; might mean "start the disk seek" what if compiler optimizes that out "because you didn't use the variable (didn't pass it to some other function)" things we don't want: 1. reordering of instructions 2. removing a "variable" access because it doesn't result in anything "useful" 3. caching something as opposed to letting it go through cache in CPU registers but not let go to memory note the use of volatile in GNU C: some_function() { volatile int *ptr1 = 0x60000000; volatile int *ptr2 = 0x60000004; int val; ... ... ... *ptr1 = 0x1212abab; ... ... ... *ptr1 = 0xabcd1234; ... ... ... val = *ptr2; /* We expect to have 0xabcd1234 assigned to val */ } this is one reason a functional approach to hw i/o can be helpful. compiler can't optimize it away ... linux provides macros in: #include to place a memory barrier between operatinos that must be visible to the hw in a particular order. void barrier(void) ... tells compiler to insert a memory barrier, does not effect hw. write registers to memory (no caching) #include void rmb(void); - read memory barrier, reads before barrier mut be completed before we go on void wmb(void); - writes must be completed void mb(void); - both read/write must be completed. insert hw memory barriers in compiler instruction flow ... what they do is platform dependent. see example. p. 229 Using I/O ports I/O ports are how drivers communicate commands to hw. 1. must allocate and free them globally in kernel. See top of p. 230. check_region() request_region() release_region() may have 8/16/32 bit ports. (architecture specific aspects) These may be in-line functions and you need to use -O to force that different functions for different ports. inb/outb etc. for 8-bit ports. see p. 230 bottom, and p. 231 even on 64-bit cpus, port-address space is still 32-bit data path. (but not bulk data itself necessarily, just commands) user space may be able to access i/o port space thru /dev/port device file. sample sources misc-porgs/inp.c and outp.c are minimal tools for i/o to ports from command-line in user space. string operations it may be possible to xfer a series of bytes (e.g., to get data in a network packet) we call these "string" instructions although they are really byte ops we can actually get a series of 8-bit bytes, words, or longs. See p. 232 for ops. pausing i/o if device is too slow, and cpu too fast, may have i/o problems. may need small delay after EVERY i/o instruction if followed by another i/o instruction pause functions like: inb_p static __inline unsigned char inb (unsigned short int port) { unsigned char _v; __asm__ __volatile__ ("inb %w1,%0":"=a" (_v):"Nd" (port)); return _v; } static __inline unsigned char inb_p (unsigned short int port) { unsigned char _v; __asm__ __volatile__ ("inb %w1,%0\noutb %%al,$0x80":"=a" (_v):"Nd" (port)); return _v; } platform dependencies using port i/o still leads to platform dependent code. IA-32: port numbers are of type unsigned short IA-64 (Itanium) ports are unsigned long and memory mapped. Alpha - ports are memory-mapped. ARM - ports are memory-mapped. ports are unsized int. M68k - ports are memory-mapped, and only byte functions are supported. port type is unsigned char *. MIPS/MIPS64, ports are memory0mapped. PowerPc - ports have type unsigned char *. ... Sparc - memory0mapped. ports are unsigned long. Using Digital I/O Ports byte-wide I/O location, either memory-mapped or port-mapped. i/o pins hooked up to register or port ... serial-i/o ... one signal with some sort of multiplexing mechanism parallel-i/o ... one pin per port/register bit an overview of the parallel port PC parallel port design: simplest mode: 1. 3 8-bit ports 0x378 is first parallel interface port 2nd at 0x278 port 1: 2-way data register (base port + 0) port 2: RO status register, reports printer status such as being online, ot of paper, busy. (base port + 1) port 3: WO contrl register, controls whether interrupts are enabled. (base port + 2) wires use standard transistor-transistor logic [0..5] volts. 1.2 volts is threshold. p. 236 has picture of logic setup. note: not all bits/pins are used. bits in boxes are pins status port: bit 7/pin 11 is busy bit. bit 6/pin 10 ACKNOWLEDGE bit 5/pin 12 paper end control port: bit 4/ interrupt enable a sample driver "short" driver reads/writes a few 8-bit ports by default uses parallel port range /dev/short0 writes/read from port 0x378 by default /dev/short1 writes/read from port base+1 by default ... /dev/short7 writes/read from port base+7 by default if you get this error: "can't get I/O address", then look in /proc/ioports to see which driver has the port/s output like so: while (count--) { outb(*(ptr++), address); wmb(); } using i/o memory i/o memory - registers and memory both appear as memory addresses (pointers to C code) NOT machine-instructions combined with ports. possible uses: 1. ethernet driver copies pkt to/from some chunk of memory This is device memory ... hw uses it directly. 2. ram video display. you put it here and it lights up the console/screen. 3. any device simply puts its registers in memory struct iodev { unsigned short *status; unsigned short *control; unsigned short cylinder, track, sector. char *destaddress; }; struct *mydisk = some address; mydisk->cylinder = 1; mydisk->track = 2; mydisk->sector = 3; mydisk->destaddress = some memory address; mydisk->status = GOCOMMAND (some bit mask); /* poll for done */ while(mydisk->status != DONEMASK) ; how you access it depends on: 1. computer 2. the bus 3. possibly MMU you may need to muck with page tables linux doesn't want you to use direct access to such memory. to aid portability/readability remember: must globally allocated device memory regions from C. 2. see code p. 239. check/allocate/release directly mapped memory a computer architecture may reserve part of its memory space for device, and disable virtual addresses in that region (yeah!) MIPs CPU has 2 address blocks of 512 MB directly mapped to physical addresses accesses bypass both MMU and computer cache *in theory*, you should use: unsigned readb(address); unsigned readw(address); unsigned readl(address); which are macros for 8/16/32 bit data values from io memory. void writeb(unsigned value, address); void writew(unsigned value, address); void writel(unsigned value, address); memset/memcpy a-likes: /* set a space to some value */ memset_io(address, value, count); (like memset()) /* copy data to/from */ memcpy_fromio(dest,source,num); memcpy_toio(dest,source,num); 64bit platforms also offer readq and writeq, for quad-word (4 shorts, 8 bytes) access reusing short for i/o memory a short fragment to do the previous job acc. to the above would be: while (count--) { writeb(*(ptr++), address); wmb(); } software-mapped i/o memory most common model is this: devices live at well-known physical addresses, but CPU has no predefined virtual address to access them. we defined physical address by: 1. hardwired in device (ISA) (or assigned by firmware in the device itself) 2. assigned by firmware at boot time (PCI) we must be able to assign a virtual address to the device. ioremap() does this - allows us to assign virtual addresses to io regions, doesn't do anything if i/o address is directly mapped See code p. 242. See code p. 243 isa memory below 1 MB PC memory range between 640KB ((xA0000) and 1 MB (0x100000). 384Kbyte range. old isa network device or video device might need this memory range. We can state that this memory range is not-directly mapped ... Actually boot page tables are created ... start by mapping physical isa addresses into kernel virtual addresses. See code p. 244 io_base can now be used with readb, etc. isa_readb and friends you can use these functions without doing the ioremap() probing for isa memory load/boot time check for said memory global resource management can't tell you if your device is really there, or if some other device is somehow there and it shouldn't be (hw problem ...) 1. find out if RAM is mapped to address 2. find out if ROM is there 3. find out if area is free note we turn off interrupts to prevent some unknown interrupt handler from mucking with the area See code p. 246