On the VIA AMOS-3000 DIO connections

Initial available information

The pin-out is plainly documented. The connector is a DE9 male, same as is conventionally used for serial ports, in fact, the AMOS-3000 also has a serial port on another DE9 on the other side of the case, along with video connectors both HMDI and a DE15 for VGA, network, and power.

Pin 1+3.3V
Pin 2GPI4
Pin 3GPI5
Pin 4GND
Pin 5NC
Pin 6+5V
Pin 7GPIO3
Pin 8GPIO2
Pin 9GND

So pins 1, 4, 5, 6, and 9 are self-explanatory. Now, where can we get software to do something exciting to and with the pins 2, 3, 7, and 8 ?

For that matter, are these inputs, outputs, or both, and if so, where is the direction control bits? Naming suggests that GPI4 and GPI5 are inputs only while GPIO2 and GPIO3 are both inputs and outputs.

Looking at the 'net, gives some others having wondered about these things on the page http://www.viaarena.com/forums/showthread.php?t=40216 and the downloaded PDF document for the programming manual, PM_CX700_202.pdf has also been found. Searching for strings GPIO2 reveals a table of GPI, GPO, and GPIO bits on page 178. This is at offset address 49-48 in PMIO.

Bits 4 5 14 and 15 are the interesting ones here:

On offset address 4D-4C in the PMIO are output bits

So the initial guess that GPIO is two-way and GPI are one-way was right.

At offset address 50 in the PMIO area are change-notification bits:

These would seem to be able to generate some kind of interrupt if bits in offset address 52 in PMIO have been set:

The hunt for the PMIO

Just finding and reading these registers would be of interest. So, where is this PMIO hiding out at? It is also identified as "ACPI I/O". On Page 51, there is a reference to something called ACPI IO Base Address Low, at offset address 0xAC (B0D0F4 RxAC), and ACPI IO Base Address High, at offset address 0xAD (B0D0F4 RxAD); these are defined on Bus 0 Device 0 Function 4 (hence B0D0F4), which appears in lspci as "CX700/VX700 Host Bridge"

Looking for PMIO further on, finds the various references to the registers including the previously seen GPI and GPIO ones. Searching then using ACPI (since the PMIO is shorthand for ACPI IO), reveals a register 0x81 in Bus 0 Device 17 Function 0, where bit 7 should be set to allow access to the ACPI I/O Block, through offsets 0x89 and 0x88. Here we have bits 15-7 being the ACPI IO Base Register, presumably related to the PMIO address, and the low bits are hardwired (R/O) as 0x01.

Oke, so to look at, say, register 0x49-0x48 in the PMIO, do we then enable access by setting bit 7 in B0D17F0 Rx81, then putting 0x48 into the high bits in B0D17F0 Rx89 ? hmm then where do we find the bits to read? I didn't see anything looking like it might be the ACPI IO data register address, unless maybe the registers B0D0F4 RxAC and B0D0F4 RxAD are these ones?

I'll see if I find something more in the kernel source. There's gotta be code there for accessing other bits in these registers, all sorts of flags and interrupt notifications and timers and on and on ... Time to go spelunking in there...

Trying setpci, it allows me to read and look at the bits. Try looking at offsets and see if they make sense as per documentation:

setpci -s 0:11.0 00.B

gives me 06

setpci -s 0:11.0 00.W

gives me the vendor identifier 1106 and

setpci -s 0:11.0 00.L

gives the first 4 octets in one go as 83241106, apparently they come in the offset order 3-2-1-0.

This is clueful; when referring to addresses in pairs like "1-0" this means the first octet read or written in a specification, and thus the bits advertised as 15-8, are the first, odd-numbered, one, and the low 8 bits are in the second, even-numbered one that is actually given as the address. The 16 bits at offsets 0x89-0x88 in the B0D17F0 thus would be specified as with:

setpci -s 0:11.0 88.W

This gave 0401, meaning, from the info on this register on page 151, the bits 000001000 0000001 of which the first ones, reading 0x008, are the ACPI IO Base register. This is presumably an address or offset, could this be that the PMIO register 0x08 is currently visible somewhere?

setpci -s 0:11.0 81.B

gives 84, indicating that bit 7 is set, and indeed, we should be able to access the ACPI IO or PMIO registers. The other bits, B3=0 indicates ACPI timer does not have 32-bit width, B2=1 indicates that RTC signals are gated during system suspend status. B1=0 indicates the time base is 30 microseconds.

PMIO registers 0x8-0xb are a timer value.

Now, what about the B0D0F4 RxAC and B0D0F4 RxAD?

setpci -s 0:4.0 0AC.W

gives 0000. Hmm... is the timer valued at 0 or are we looking at something else. And what does the Device 0 Function 4 have to do with this anyways?

The port hypothesis

The programming manual refers to some Power Management System Overview, but none such seems to exist, nor does it seem to be anything on the `net.

Or is it just that we read and write the PMIO array beginning at port 0x401, so offset 0 is 0x401, offset 0x49-0x48 would be at 0x44a-0x449? Seems wrong with the alignment though...

Investigations of the SMBUS (i2c) driver code, comparing it with the specs, show that the base address there is defined in an IO space area: the base-address at B0D17F0 RxD1-D0, and enabled if bit B0D17F0 RxD2[2], SMBEN, bit is set.

This looks very similar to what the documentation says about the PMIO stuff: access is enabled if B0D17F0 Rx81[7] is 1, (which it is), and we put out the base-address (or use the one that is already been set up) at B0D17F0 Rx89-88. So what happens if we look at IO locations offset from port 0x401?

To recapture, offsets 0x49-48 Bits 4 5 14 and 15 are the interesting ones here, with the hypothesized offsets:

Pin 2 Bit 4 GPI4 0x48 bit 4 port 0x449? bit 4
Pin 3 Bit 5 GPI5 0x48 bit 5 port 0x449? bit 5
Pin 7 Bit 14 GPIO2 0x49 bit 6 port 0x44a? bit 6
Pin 8 Bit 15 GPIO3 0x49 bit 7 port 0x44a? bit 7

Hardware connections made

Time for a voltmeter and a C program running userspace IO access methinks.

A DE9 female connector with 10K resistors connected from pin 1 (3.3V) to each of the 4 signal inputs, pins 2,3,7, and 8. Two 220 Ohm resistors connected to pins 1 and 4 (GND) and having a loose end, to provide H and L voltage references, and allow for short-circuit accidents not having deleterious effects.

The voltmeter and ampere-meter is used to determine voltage levels, and current-draw of the inputs. Pulling the GPI4 or GPI5 low requires 0.7 mA, the GPIO2 and GPIO3 require 1.6 mA -- same as standard TTL. No investigations yet done on threshold values, that will have to be after the matching input bits are located.

Testing Software made

It turns out that the documentation for userland pci is way out of date. The manpage refers to functions called pciconfig_read, and the #include file which it calls pci.h is actually sys/pci.h, but then this enterprise founders on the absence of any library to link with -- the only files in /usr/lib or /lib that even contains a string that resembles this, is /lib/libaudit.so.0 -- but these apparently are not entry points. This is pretty up to date Debian by the way....

But the package for libpci-dev provides a more updated set of functions to deal with pci -- besides, the lspci and setpci programs work from out of userspace, so some kind of library is demonstrably in effect. Just that there isn't much documentation beyond the #include files, and source code once its whereabouts are figured out!

Voltmeter and current meter shows all pins at H, pulled up to the 3.3V line, the GPIO draw about 1.6 mA when pulled to ground, the GPI lines pull 0.7 mA when pulled to ground. They are pulled high with 10K resistors. Pulling pin 3 L seems to trigger a kind of suspend-mode, so this might be less than useful.

Never mind any of that, just try reading ports from 0x401 onwards and see what they look like and see what changes when pulling the lines low. Fortunately, the inb_p() and outb_p() calls declared in <sys/io.h> do exist in some standard library, so does the required call to iopl(). Have to run as root for now, which is rather messy; but once the addresses and such are figured out, make a kernel-module and device that can be hit with ioctl calls or some such, like was done with ISA-bus hardware such as the AD-converters and a parallel IO card in the past.

Scanning port at addresses from 0x401 up to 0x45D reveals changes to 0x408, 0x409, 0x40a, 0x449, and 0x450 between all pins H and pin 7, GPIO3, pulled L. Going back makes 0x449 revert to the previous state, 0x450 stays different, but 0x408, 0x409, and 0x40a continue to change. Looking at the documentation, there is something called the extended timer living at offsets 0x08 to 0x0b, so it makes sense that there would be changes in this area. Seems we may be on the right track.

Trying these bits then give the following changes in various registers:

GPIO3GPIO2GPI4GPI5
Pin Pin Pin Pin RegisterRegisterRegisterRegister
7 8 2 3 0x449 0x450 0x448 0x421
H H H H 0xfe 0x00 0xf6 0x00
L H H H 0x7e 0x80 0xf6 0x00
H H H H 0xfe 0x80 0xf6 0x00
H L H H 0xbe 0xc0 0xf6 0x00
H H H H 0xfe 0xc0 0xf6 0x00
H H L H 0xfe 0xc0 0xe6 0x10
H H H H 0xfe 0xc0 0xf6 0x10
H H H L         causes a suspend-like action!

So the register 0x449 bits 7 and 6 change with pins 7 and 8, (GPIO 3 and 2), and register 0x48 bit 4 changes with pin 2, (GPI4) which seems to be close to what is expected in offsets 0x49 and 0x48. These registers are read-only, the output bits are elsewhere.

As the GPIs and GPIOs seem to map onto the expected bits in these registers, this means that even if the base-address found in B0D17F0 Rx89-88 reads 0x401, the actual base address is 0x400, for whatever reason that additional value 1 is hardcoded in there.

So the correct value would be had by reading B0D17F0 Rx89-88 and then AND it with 0xFF80 so as to zero out the lowest bits, 6 to 0.

Bits 7 and 6 in 0x450 and bit 4 in 0x421, however, what do they do?

0x450 is the GPIO change status, and bits 7 and 6 will be set, and remain set, when GPIO3 and GPIO2 have their level changing. These are reset by writing a 1 to their bits.

0x421 bit 4, advertised as 0ffsets 0x21-0x20 bit 12, indicates that an input called BATLOW# is pulled Low... so GPI4 is linked to this flag somewhere somehow. Like the bits in 0x450, this is also reset by writing a 1 to it. This linkage is shown in the description of register 0x49-0x48, where the various GPI and GPIO lines used as inputs are shown. GPI4 comes from BATLOW#, GPI5 is also EXTSMI# and so from the description of 0x420, the bit 4 here should also be set when this line is asserted Low, then can be reset in the same way.

Somewhere in the kernel the EXTSMI# is set to do something like a suspend... Apparently bit 4 in 0x423-0x422 enables this, and that bit is set to 1. This defaulted to 0, so somewhere in the kernel is a place where it gets set. If we can catch this SCI/RESUME signal and use it for something that could be useful... The register 0x452 bits 7 and 6 seem to allow enabling SMI on GPIO3 and GPIO2, these bits are at their default 0 for disabled. Good thing.

It is also interesting to note that while 0x449-0x448 indicate input levels, this is read-only. The output levels are apparently set in 0x44d-0x44c.

And the two GPIOs are documented as being Open Drain; they will work as inputs if set to High by writing 1 to their bits into 0x44d. Current value of 0x44d is 0xff thus making these two both inputs. Setting bits 7 and 6 here to 0 should cause these wires to go L. And indeed, a program with a loop that does this works as expected, setting bits 7 and 6 of 0x44d flipping them up and down does the job.

Conclusions

To summarize the interesting locations found:

Enable-bit at PCI B0D17F0 Rx81 bit 7

(PCI Bus 0 Device 17 Function 0 Register 0x81) where bit 7 should be set to allow access to the PMIO

Base address at PCI B0D17F0 Rx89-88

(PCI Bus 0 Device 17 Function 0 Registers 0x89-0x88) Read 0x88 as a 16-bit quantity, which will pull the 0x89 into the high 8 bits, and AND that with 0xFF80 so as to zero out the 7 lowest bits, 6 to 0.

Subsequent PMIO offset references are the IO ports starting at this base address, so 0x49 for example will become the port ba+0x49. If this value found in 0x89-0x88, after ANDing, is 0x400, the GPI02 and GPIO3 input states will be found at port 0x449.

Pin 7, GPIO2, input or open drain output

Pin 8, GPIO3, input or open drain output

Pin 2, GPI4, input only

Pin 3, GPI5, input only

On the way to a device driver

Now for making some kind of kernel module-device driver, or discovering existing ioctls that could be used.

Based on a previous simple driver for input and output ports, it was first a matter of getting the code to compile and link into a workable module. By pulling down the kernel source, and re-compiling it beginning with make oldconfig, then make, them make prepare_modules, it became an easy matter of creating the properly acceptable module.

Now for the PCI-action; I found out that the function pdev = pci_get_bus_and_slot(0, PCI_DEVFN(17, 0)) was helpful, then pci_bus_read_config_byte() and then pci_bus_read_config_word() to find the values at 0x81 and 0x88-0x89 and, last, and not to forget, release the access with pci_dev_put() afterwards.

The values obtained were the expected ones, a check on bit 7 and the ANDing of the base address followed.

Then for the IO, call request_region() for the range 0x400-0x453 and this was denied. Hmm, of course, something else is using this, oh, how do we make our way around that?

Looking at /proc/ioports, this showed:

0400-047f: pnp 00:01
  0400-0403 : ACPI PM1a_EVT_BLK
  0404-0405 : ACPI PM1a_CNT_BLK
  0408-040b : ACPI PM_TMR
  0420-0423 : GPEO_BLK
  0450-0453 : GPE1_BLK

and so instead of grabbing everything, if we behave more modest, and just grab the two pairs of ports at 0x448-0x449 and 0x44c-0x44d, we will get somewhere. That makes the input and output bits available, but the change bits in 0x450 and 0x420 and 0x421 are maybe off-limits. Still, this is something to begin with and may be enough for a while.

When the module now is loaded, its port requests show up in /proc/ioports as well.

The names of these allocations appear in the source file drivers/acpi/osl.c, the calls to request_region() for these addresses are there. So an alternative would be to have it request these regions as well, and piggyback onto it.

Hmm -- what if we request the region 0450-0453 with the same name? Will that work? No... it does not work. To do anything more exciting than reading GPI4 (Pin 2) and reading and writing GPIO3 (Pin 8) and GPIO2 (Pin 7) we will have to piggyback onto the ACPI code, in the same environment as the osl.c code.

On the other hand, as this memory area is already requested, can't we just hit on it? Try that, with an inb(0x450). Yes, that returned 0xc0, as we would expect as leftovers from previous experiments. And writing 0xc0 to it, to clear the bits, also worked. Cool!

We could also change the bit 4 in 0x422 so that the GPI5 (Pin 3) doesn't issue any SMI anymore. Guess this is somewhat more dangerous than just hitting the change flags in 0x50 (and 0x20 and 0x21)

So we can put in some action into read and write functions: writing an octet to this device sets the bit values on the two GPO bits. Reading returns the bit states from the inputs. Change flags and reset on these (and maybe also waiting for them, as we see on serial ports) as well as the SMI turn-off/turn-on thing, are good candidates for ioctl() calls.

device amosdio major 61

Yes, I've got my character device driver working. It sets values of the two bits when writing and reads the values of the 4 bits when reading. There are ioctl() calls for checking and resetting the change flags, and turn the SMI on GPI5 off or on. All of the ioctls are defined in the file amosdio.h -- a file used both by the module source inside the kernel and the userland code on the outside.

I gave the module the name amosdio. I have put up a tarball amosdio-20101216.tar.gz with the code in it if anyone should be interested. This works on Debian and kernel 2.6.32 as that is where I used it. The driver uses device major number 61, as this is one of the unassigned ones that can be freely used.

It will do me well for a while; later I might want to hook it into the i2c subsystem or something along those lines; the GPIO2 and GPIO3 are the candidates for operation as the SDA and SCL lines... Then a PCF8574 and so on for expansion.... Or maybe some kind of Picotux-style GPIO expansion arrangement here. Possibilities abound.


Back to the index page

KRL 2010-12-15.