I am trying to get the GPIO pins on my mainboard working but I don't know the numbers/descriptions of the pins and if a driver is loaded or not.

I found this guide where it says that I should echo the pin description and redirect the output into /sys/class/gpio/export

/sys/class/gpio # echo 0 > export

So I did. The /sys/class/gpio/ folder exists on my system and it contains the following:

[user@host ~]$ ls -l /sys/class/gpio/
total 0
--w------- 1 root root 4096 Nov 30 18:12 export
--w------- 1 root root 4096 Nov 30 18:12 unexport
[user@host ~]$ 

(By the way if I see this folder does it mean a driver is loaded?)
Then I tried several pin names from the datasheet of my mainboard but I always get the following

[root@host gpio]# echo 31 > export 
echo: write error: Invalid argument
[root@host gpio]# 

I am using Arch Linux and my kernel version is 4.19.2-arch1-1-ARCH.
The Mainboard I use is a Supermicro X10SBA. https://www.supermicro.com/products/motherboard/celeron/x10/x10sba.cfm

The only thing I can find about GPIOs on the Motherboard is on Page 2-25 in the X10SBA Mainboard Datasheet :

1 +3.3V
2 SOC_P3V3_GPIO_S5_31
3 SOC_P3V3_GPIO_S5_32
4 SOC_P3V3_GPIO_S5_33
5 SOC_P3V3_GPIO_S5_34
6 SOC_P3V3_GPIO_S5_35
7 SOC_P3V3_GPIO_S5_36
8 SOC_P3V3_GPIO_S5_37
9 SOC_P3V3_GPIO_S5_38
10 GND

From my understanding it means the board has eight GPIOs.

So I tried to echo various different combinations into /sys/class/gpio/export like

echo SOC_P3V3_GPIO_S5_31 > export
echo 111 > export
echo 531 > export
echo S531 > export
echo S5_31 > export

And so on. Nothing worked.
Am I doing something fundamentally wrong here? Where are the definitions of these GPIO names? Where does export get its information from? Do I have to make these definitions myself first? Maybe do I have to recompile my kernel?

I went on and tried to find out which chip is used for the GPIO headers.

Next to the GPIO Pin headers (JP1) on the board, there is a Chip NXP GTL2010 (Datasheet) and the pins are connected to it from D1 through to D8. I guess this chip is just doing some voltage translation to make the output 3.3V or 5V. So the signal must be coming from The S1-S8 pins. Unfortunately I could not find where these source pins of the GTL2010 are connected to as the traces lead to vias on the board.

But I assume the pins go directly to the CPU. I'm not 100% sure on that. But its my assumption.

The CPU on board is an Intel Celeron J1900. But unfortunately I couln't find a datasheet or any information about it if it has GPIOs.

Is there some possibility to list the GPIOs on board in Linux? How can I check if there is already some GPIO driver used on my system?


After some research in the CPU datasheet I found that the GPIO_BASE_ADDRESS register (datasheet p. 1219) needs to be set in order to change GPIO settings. The register expects the base address in the I/O space where the GPIO logic is located. Now I don‘t understand which address I should put here. Can this be any 256 free bytes in the I/O space?

Besides that I don’t understand how to access the GPIO_BASE_ADDRESS register. The datasheet states that the register is in the PCI Configuration Space. Bus 0, Device 31 (hex 1f), Function 0. (Datasheet page 56)

Now lspci gives me the following output for device 31:

[user@host ~]$ sudo lspci -vvvvvv
00:1f.0 ISA bridge: Intel Corporation Atom Processor Z36xxx/Z37xxx Series Power Control Unit (rev 0e)
Subsystem: Super Micro Computer Inc Atom Processor Z36xxx/Z37xxx Series Power Control Unit
Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0
Capabilities: [e0] Vendor Specific Information: Len=0c <?>
Kernel driver in use: lpc_ich
Kernel modules: lpc_ich

00:1f.3 SMBus: Intel Corporation Atom Processor E3800 Series SMBus Controller (rev 0e)
Subsystem: Super Micro Computer Inc Atom Processor E3800 Series SMBus Controller
Control: I/O+ Mem+ BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
Status: Cap+ 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
Interrupt: pin B routed to IRQ 18
Region 0: Memory at 90a04000 (32-bit, non-prefetchable) [size=32]
Region 4: I/O ports at e000 [size=32]
Capabilities: [50] Power Management version 3
Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA PME(D0-,D1-,D2-,D3hot-,D3cold-)
Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=0 PME-
Kernel driver in use: i801_smbus
Kernel modules: i2c_i801

I am not sure how to access this area to write a base address to the register and I don‘t know where in IO space I should put my registers. And how do I realize this in C? Can I make use of existing Linux funtions to implement this?


The hexdump of the configuration space outputs the following:

[user@host ~]$ sudo lspci -xxx -s 00:1f.0
[sudo] password for user: 
00:1f.0 ISA bridge: Intel Corporation Atom Processor Z36xxx/Z37xxx Series Power Control Unit (rev 0e)
00: 86 80 1c 0f 07 00 10 02 0e 00 01 06 00 00 80 00
10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
20: 00 00 00 00 00 00 00 00 00 00 00 00 d9 15 16 08
30: 00 00 00 00 e0 00 00 00 00 00 00 00 00 00 00 00
40: 03 04 00 00 02 30 d0 fe 03 05 00 00 02 c0 d0 fe
50: 02 80 d0 fe 02 10 d0 fe 02 00 f0 fe 02 50 d0 fe
60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
d0: 00 00 00 00 00 00 00 00 cf ff 00 00 00 00 00 00
e0: 09 00 0c 10 00 00 00 00 00 00 00 00 00 00 00 00
f0: 01 c0 d1 fe 00 00 00 00 1a 0f 0e 01 03 03 00 00

[user@host ~]$

From my interpretation the ACPI_BASE_ADDRESS register (manual page 1217) contains „03 04 00 00“.

The GPIO_BASE_ADDRESS register (manual page 1219) contains „03 05 00 00“.

Grepping for acpi in dmesg returns several entries (too much to post here).

/proc/ioports outputs the following:

[user@host ~]$ sudo cat /proc/ioports 
[sudo] password for user: 
0000-006f : PCI Bus 0000:00
  0000-001f : dma1
  0020-0021 : pic1
  0040-0043 : timer0
  0050-0053 : timer1
  0060-0060 : keyboard
  0064-0064 : keyboard
0070-0077 : PCI Bus 0000:00
  0070-0077 : rtc0
0078-0cf7 : PCI Bus 0000:00
  0080-008f : dma page reg
  00a0-00a1 : pic2
  00c0-00df : dma2
  00f0-00ff : fpu
  02e0-02e7 : serial
  02f8-02ff : serial
  03e0-03e7 : serial
  03f8-03ff : serial
  0400-047f : pnp 00:01
    0400-0403 : ACPI PM1a_EVT_BLK
    0404-0405 : ACPI PM1a_CNT_BLK
    0408-040b : ACPI PM_TMR
    0420-042f : ACPI GPE0_BLK
    0430-0433 : iTCO_wdt.0.auto
      0430-0433 : iTCO_wdt
    0450-0450 : ACPI PM2_CNT_BLK
    0460-047f : iTCO_wdt.0.auto
      0460-047f : iTCO_wdt
  0500-05fe : pnp 00:01
  0600-061f : pnp 00:01
  0680-069f : pnp 00:01
  0a30-0a3f : pnp 00:07
0cf8-0cff : PCI conf1
0d00-ffff : PCI Bus 0000:00
  1000-1fff : PCI Bus 0000:01
  b000-cfff : PCI Bus 0000:03
    b000-cfff : PCI Bus 0000:04
      b000-bfff : PCI Bus 0000:07
        b000-b01f : 0000:07:00.0
          b000-b01f : ahci
        b020-b023 : 0000:07:00.0
          b020-b023 : ahci
        b030-b037 : 0000:07:00.0
          b030-b037 : ahci
        b040-b043 : 0000:07:00.0
          b040-b043 : ahci
        b050-b057 : 0000:07:00.0
          b050-b057 : ahci
      c000-cfff : PCI Bus 0000:05
        c000-c01f : 0000:05:00.0
  d000-dfff : PCI Bus 0000:02
    d000-d01f : 0000:02:00.0
  e000-e01f : 0000:00:1f.3
    e000-e01f : i801_smbus
  e020-e027 : 0000:00:02.0

2 Answers 2


In general, GPIO pins are highly hardware specific. There are no systematic names, no systematic drivers, no systematic registers.

The only thing you can do is read the information you have, google, and guess.

As your motherboard manual says there is a GPIO header, we can assume that the GPIO pins are actually physically routed somehwere (that's not a given; the GPIO pins may be used for other stuff by the BIOS, or they may just be open). "SOC" means "System on a Chip", "3V3" means 3.3 Volts (the TTL level).

First, a warning: If this header directly connects to the SoC, then it's easy to damage your SoC by doing the wrong things. Electrostatic discharge, wrong voltage levels, confusing inputs with outputs etc. may damange your SoC, and your main CPU with it. Always connect a buffer chip to it first if you want to use it. Even if the buffer chip you found next to it actually secures this header, and not something else.

So now we need a datasheet. Googling turns up this, which looks pretty good.

It tells us that the SoC has 101 GPIO pins for S0, and 43 GPIO pins for S5. Only 10 of those end up on the motherboard header, but thankfully we know which ones. The other ones may be connected to other things on the motherboard, so it's important to leave them alone.

On page 56 it tells us that the GPIO (PCU) is a 256-byte long moveable I/O range, decoded by a PCI device on the I/O fabric. I'm not sure how to interpert GBA: PCI[B:0,D:31,F:0] + 48h. In any case, that means the next step is to use lspci as verbosely as necessary, figure out which PCI devices could be meant, and look for a region of 256 bytes that looks promising.

Further on it tells us about the PCU (Platform Unit Controller), in particular about the GPIO registers, starting on page 1262.

So the next step is to read all this, understand it, write a kernel driver for it that uses the correct PCI card and region. This driver will make the I/O pins appear under /sys/class/gpio. Writing one shouldn't be too difficult, googling existing GPIO drivers and modifying them for this hardware should be enough. You need to know how to program in C, and you need to be able to teach yourself how to write a kernel module.

It's also possible that there already exists a driver for this particular hardware, but at least it's not in your kernel (or you'd already see some pins).


Ok, B, D and F seem to match the bus, device and function of the PCI devices, and there are two 32-byte regions for the SMBus controller, so at least one will match the one described in the manual.

However, there are no regions on 00:1f.0, only a vendor specific block. The data sheet says +40 for ACPI power management, +48h for GPIO, and +f0h for the RCBA, and it also says "They are set using base address registers (BARs) or other similar means", so maybe it's not a BAR/region, but just bytes in the PCI configuration.

So try something like lspci -xxx -s 00:1f.0 (as root), this should show the whole configuration space as hexdump. Also have a look at dmesg after boot and cat /proc/ioports to see of the ACPI power management shows up somewhere (i.e., if it has any I/O port ranges associated to it). We can compare this to +40h. Please edit question with the info.

If the GPIO range is not in a BAR/region, it will get extremely hacky to enable it; at this point you probably should start writing the kernel driver.


The Coreboot project also has code to access GPIO stuff on Intel, e.g. gpio.c and gpio.h for the Baytrail-Architecture. Not sure which architecture the Celeron J1900 is, but even if it doesn't match, it may give hints are the GPIO region works.


Ok, the BARs are indeed completely empty. Assuming +40h and +48h work like bars, they are both I/O space (lowest bit is one), where

+40h (ACPI) = 0400h
+48h (GPIO) = 0500h 

Comparing with /proc/ioports, this makes sense: ACPI is 0400-047f, and 0500-05fe has been reserved for the same device pnp 00:01.

So that's the I/O range, and it is already mapped. You can access it via /dev/port, reading and writing at the correct offset, or in a C program using ioperm. Though I vaguely remember the Linux kernel developers threatened to disable either one or both of those features, so I don't know if it still works. In that case, you need a kernel driver.

In any case, be really careful when working with the I/O space: Even reading the wrong address can cause hardware actions, and if you do that on random addresses, anything can happen. So no hexdump -C /dev/port. Also, access size matters.

I couldn't find anything concrete in the datasheet how the GPIO I/O space works, so either you need to google a better datasheet for that, or possibly the Coreboot files work similarly enough.

  • 1
    Thanks your answer was really helpful. I guess GBA: PCI[B:0,D:31,F:0] + 48h describes the position of the GPIO_BASE_ADRESS on Bus 0, Device 31, Function 0 with an offset of hex 48. See the datasheet page 1219. From my understanding an address of the IO space must be entered here, where the actual GPIO registers are defined. I guess that would be the „movable I/O range“? I don‘t know what I should write to this register and how I can access it. When I call lspci on my system, the entry 00:1f.0 ISA brige: Intel..Pow. Contr. Unit looks very much like the one I need. But how can I access it? Dec 3, 2018 at 9:49
  • Use lspci -vvv etc. with as many v as necessary, as root if necessary, until you see the regions. If there's an 256 byte region, then very likely that's the one. Edit the question with the output if you are not sure. Also check other PCI devices, though "ISA bridge" is a good candidate.
    – dirkt
    Dec 3, 2018 at 9:51
  • I have edited the question Dec 3, 2018 at 10:41
  • Please do cat /proc/ioports as root (forget to mention it, sorry), so we see the actual ranges instead of 0000-0000. I'll have a closer look at the PCI config when I have the time.
    – dirkt
    Dec 3, 2018 at 14:52
  • Okay, I updated the post. Thanks for your time. Dec 3, 2018 at 15:05

I acquired similar hardware and faced similar issues. At first I tried to explore the route described by others, but then I solved it as follows:

My system: CPU Version: Intel(R) Celeron(R) CPU J1900 @ 1.99GHz

At some point I figured that this implements the IT8786 Chip.

If you check your Kernel Configuration, you might find support for the IT87x family of drivers (under GPIO Drivers of course). In my case this was only enable as a Module.

Then, I loaded the module with:

sudo insmod /lib/modules/5.8.0-55-generic/kernel/drivers/gpio/gpio-it87.ko

dmesg returned the following item:

gpio_it87: Found Chip IT8786 rev 2. 64 GPIO lines starting at 0a00h

By checking /dev, gpiochip0 appeared and from there on you can use the already mentioned sysfs method if you wish so (although in retirement).

If you don't know how to relate the physical pins to the hardware, then try this:

apt-get install gpiod

Running gpioinfo returns in my case the following:

gpiochip0 - 64 lines:
        line   0:  "it87_gp10"       unused   input  active-high
        line   1:  "it87_gp11"       unused   input  active-high
        line   2:  "it87_gp12"      "sysfs"   input  active-high [used]
        line   3:  "it87_gp13"       unused   input  active-high
        line   4:  "it87_gp14"       unused   input  active-high
        line   5:  "it87_gp15"       unused   input  active-high
        line   6:  "it87_gp16"       unused   input  active-high
        line   7:  "it87_gp17"       unused   input  active-high
        line   8:  "it87_gp20"       unused   input  active-high
        line   9:  "it87_gp21"       unused   input  active-high
        line  10:  "it87_gp22"       unused   input  active-high
        line  11:  "it87_gp23"       unused   input  active-high
        line  12:  "it87_gp24"      "sysfs"   input  active-high [used]
        line  13:  "it87_gp25"       unused   input  active-high
        line  14:  "it87_gp26"       unused   input  active-high
        line  15:  "it87_gp27"       unused   input  active-high
        line  16:  "it87_gp30"       unused   input  active-high
        line  17:  "it87_gp31"       unused   input  active-high
        line  18:  "it87_gp32"       unused   input  active-high
        line  19:  "it87_gp33"       unused   input  active-high
        line  20:  "it87_gp34"       unused   input  active-high
        line  21:  "it87_gp35"       unused   input  active-high
        line  22:  "it87_gp36"      "sysfs"  output  active-high [used]
        line  23:  "it87_gp37"       unused   input  active-high
        line  24:  "it87_gp40"       unused   input  active-high
        line  25:  "it87_gp41"       unused   input  active-high
        line  26:  "it87_gp42"       unused   input  active-high
        line  27:  "it87_gp43"       unused   input  active-high
        line  28:  "it87_gp44"       unused   input  active-high
        line  29:  "it87_gp45"       unused   input  active-high
        line  30:  "it87_gp46"       unused   input  active-high
        line  31:  "it87_gp47"       unused   input  active-high
        line  32:  "it87_gp50"       unused   input  active-high
        line  33:  "it87_gp51"       unused   input  active-high
        line  34:  "it87_gp52"       unused   input  active-high
        line  35:  "it87_gp53"       unused   input  active-high
        line  36:  "it87_gp54"       unused   input  active-high
        line  37:  "it87_gp55"       unused   input  active-high
        line  38:  "it87_gp56"       unused   input  active-high
        line  39:  "it87_gp57"       unused   input  active-high
        line  40:  "it87_gp60"       unused   input  active-high
        line  41:  "it87_gp61"       unused   input  active-high
        line  42:  "it87_gp62"       unused   input  active-high
        line  43:  "it87_gp63"       unused   input  active-high
        line  44:  "it87_gp64"       unused   input  active-high
        line  45:  "it87_gp65"       unused   input  active-high
        line  46:  "it87_gp66"       unused   input  active-high
        line  47:  "it87_gp67"       unused   input  active-high
        line  48:  "it87_gp70"       unused   input  active-high
        line  49:  "it87_gp71"       unused   input  active-high
        line  50:  "it87_gp72"       unused   input  active-high
        line  51:  "it87_gp73"      "sysfs"   input  active-high [used]
        line  52:  "it87_gp74"      "sysfs"  output  active-high [used]
        line  53:  "it87_gp75"      "sysfs"   input  active-high [used]
        line  54:  "it87_gp76"      "sysfs"   input  active-high [used]
        line  55:  "it87_gp77"       unused   input  active-high
        line  56:  "it87_gp80"       unused   input  active-high
        line  57:  "it87_gp81"       unused  output  active-high
        line  58:  "it87_gp82"       unused   input  active-high
        line  59:  "it87_gp83"       unused   input  active-high
        line  60:  "it87_gp84"       unused   input  active-high
        line  61:  "it87_gp85"       unused   input  active-high
        line  62:  "it87_gp86"       unused   input  active-high
        line  63:  "it87_gp87"       unused   input  active-high

You can use the utilities of gpiod to work with the different pins. If your manufacturer has in some way indicated which GPI/O you can use, then you can related the "line" to the it87_gpXX" part, being XX the pins probably provided by the manufacturer.

A quick check can be done on the old way.

Go to /sys/class/gpio and see what gpiochip you find. The base number inside the gpiochip is the start position and the line points returned by gpioinfo the offset.

In my case I had 448 as base. If I want to reach GP81, that is line 57, so I would do this:

echo 505 > /sys/class/gpio/export

I hope this helps! It saved me from writing the Driver (By the way, the manufacturer sent me a driver but it didn't work)

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .