Skip to content

Instantly share code, notes, and snippets.

@depau
Created October 17, 2025 21:19
Show Gist options
  • Select an option

  • Save depau/9fed6a67c44109c3c3194864771ac663 to your computer and use it in GitHub Desktop.

Select an option

Save depau/9fed6a67c44109c3c3194864771ac663 to your computer and use it in GitHub Desktop.
Kobo Touch PoC Linux mainline device tree

Proof-of-Concept device tree source for Kobo Touch N905C for mainline Linux

This is a bootable device tree for the Kobo Touch. I managed to make it boot but then I got bored, so here it is for you to play.

It is definitely incomplete and probably partially incorrect, but it boots and it is able to mount a rootfs from either SD card.

Interrupts are all commented-out since I haven't fully figured them out. The values I specified are incorrect and enabling them causes the kernel to freeze, most likely because I'm incorrectly wiring them to floating GPIOs causing an interrupt storm which saturates the CPU time.

Note that mainline Linux has no driver for the touch screen and e-paper display, so while the system will boot you cannot display anything.

I also wrote a simple decoder for the configuration blob passed by the bootloader, you can play with it on Compiler Explorer You can dump the blob with devmem from busybox or devmem2 https://bakhi.github.io/devmem/

Code has been written by reading board files from the InkBox kernel.

Most important board files (in arch/arm/mach-mx5):

  • mx50_rdp.c
  • mx50_ntx_io.c

Code licensed under the GPL v2.0 license like the Linux kernel.

/dts-v1/;
#include "imx50.dtsi"
#include <dt-bindings/input/input.h>
#include <dt-bindings/interrupt-controller/irq.h>
/ {
model = "Kobo Touch N905C"; /* board ID E60612 in mx50_rdp.c */
compatible = "kobo,n905c", "fsl,imx50";
chosen {
stdout-path = "serial1:115200n8"; /* early_console_setup on UART2 at mx50_rdp.c line 2667 */
};
memory@70000000 {
device_type = "memory";
reg = <0x70000000 0x10000000>; /* 256 MB DRAM like other Kobo boards */
};
gpio-leds {
compatible = "gpio-leds";
led-on {
label = "n905c:orange:on";
gpios = <&gpio1 24 GPIO_ACTIVE_LOW>; /* GPIO_LED_ON from mx50_ntx_io.c line 51 */
panic-indicator;
};
};
gpio-keys {
compatible = "gpio-keys";
key-power {
label = "Power Button";
gpios = <&gpio4 10 GPIO_ACTIVE_LOW>; /* GPIO_PWR_SW from mx50_ntx_io.c line 54 */
linux,code = <KEY_POWER>;
};
event-hallsensor {
label = "Hallsensor";
gpios = <&gpio5 25 GPIO_ACTIVE_LOW>; /* HALLSENSOR_KEY from mx50_ntx_io.c line 1664 */
linux,code = <KEY_RESERVED>;
linux,input-type = <EV_SW>;
};
};
sd3_pwrseq: pwrseq {
compatible = "mmc-pwrseq-simple";
reset-gpios = <&gpio5 14 GPIO_ACTIVE_LOW>; /* GPIO_WIFI_RST from mx50_ntx_io.c line 56 */
};
sd3_vmmc: regulator {
compatible = "regulator-gpio";
regulator-name = "vmmc";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sd3_vmmc>;
states = <3300000 0>;
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
enable-gpios = <&gpio4 12 GPIO_ACTIVE_HIGH>; /* GPIO_WIFI_3V3 from mx50_ntx_io.c line 55 */
startup-delay-us = <100000>;
};
};
&kpp {
status = "okay";
linux,keymap = <
MATRIX_KEY(0x00, 0x00, KEY_HOME)
MATRIX_KEY(0x01, 0x00, KEY_POWER)
MATRIX_KEY(0x02, 0x00, KEY_RESERVED)
MATRIX_KEY(0x03, 0x00, KEY_RESERVED)
MATRIX_KEY(0x00, 0x01, KEY_RESERVED)
MATRIX_KEY(0x01, 0x01, KEY_RESERVED)
MATRIX_KEY(0x02, 0x01, KEY_RESERVED)
MATRIX_KEY(0x03, 0x01, KEY_RESERVED)
MATRIX_KEY(0x00, 0x02, KEY_RESERVED)
MATRIX_KEY(0x01, 0x02, KEY_RESERVED)
MATRIX_KEY(0x02, 0x02, KEY_RESERVED)
MATRIX_KEY(0x03, 0x02, KEY_RESERVED)
MATRIX_KEY(0x00, 0x03, KEY_RESERVED)
MATRIX_KEY(0x01, 0x03, KEY_RESERVED)
MATRIX_KEY(0x02, 0x03, KEY_RESERVED)
MATRIX_KEY(0x03, 0x03, KEY_RESERVED)
MATRIX_KEY(0x00, 0x04, KEY_RESERVED)
>;
};
&esdhc1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sd1>;
bus-width = <4>;
max-frequency = <50000000>;
// cd-gpios = <&gpio1 27 GPIO_ACTIVE_LOW>; /* SD1_CD from mx50_rdp.c line 2612 */
non-removable;
disable-wp;
status = "okay"; /* internal µSD */
};
&esdhc2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sd2>;
bus-width = <4>;
max-frequency = <50000000>;
cd-gpios = <&gpio5 17 GPIO_ACTIVE_LOW>; /* SD2_CD from mx50_rdp.c line 2614 */
disable-wp;
status = "okay"; /* external µSD */
};
&esdhc3 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sd3>;
bus-width = <4>;
max-frequency = <25000000>; /* mmc_wifi_data.max_clk at mx50_rdp.c line 1851 */
non-removable;
disable-wp;
mmc-pwrseq = <&sd3_pwrseq>;
vmmc-supply = <&sd3_vmmc>;
status = "okay"; /* SDIO Wi-Fi */
// TODO: wi-fi + interrupt
/* CyberTan WC121 SDIO WiFi (BCM43362) */
};
&i2c1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
clock-frequency = <400000>;
status = "okay";
zforce@50 {
compatible = "neonode,zforce";
reg = <0x50>; /* from mxc_i2c0_E60612_board_info */
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ts>;
// interrupts-extended = <&gpio5 15 IRQ_TYPE_EDGE_FALLING>; /* TOUCH_INT line 130 */
irq-gpios = <&gpio5 15 GPIO_ACTIVE_LOW>; /* TOUCH_INT line 130 */
reset-gpios = <&gpio5 26 GPIO_ACTIVE_LOW>; /* IR_TOUCH_RST line 1688 */
/* zforce_i2c.c lines 503-518 - device reports bDisplayResolution==0 */
touchscreen-min-x = <0>;
touchscreen-size-x = <800>;
touchscreen-min-y = <0>;
touchscreen-size-y = <600>;
};
};
&i2c3 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c3>;
clock-frequency = <100000>;
status = "okay";
netronix@43 {
compatible = "netronix,ntxec";
reg = <0x43>; /* mxc_i2c2_board_info address */
// interrupts-extended = <&gpio4 11 IRQ_TYPE_EDGE_FALLING>; /* GPIO_MSP_INT line 58 */
system-power-controller;
};
};
&uart2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart2>;
status = "okay"; /* console UART */
};
&usbphy0 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usbphy>;
vbus-detect-gpio = <&gpio4 19 GPIO_ACTIVE_LOW>; /* USB_OTG_PWR from mx50_rdp.c line 128 */
};
&usbotg {
dr_mode = "otg";
disable-over-current;
phy_type = "utmi_wide";
status = "okay";
};
&cspi {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_cspi>;
cs-gpios = <&gpio4 24 GPIO_ACTIVE_HIGH>,
<&gpio4 25 GPIO_ACTIVE_LOW>;
status = "okay";
pmic: mc13892@0 {
compatible = "fsl,mc13892";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pmic>;
spi-max-frequency = <6000000>;
spi-cs-high;
reg = <0>;
// interrupts-extended = <&gpio1 8 IRQ_TYPE_LEVEL_HIGH>;
fsl,mc13xxx-uses-adc;
fsl,mc13xxx-uses-rtc;
fsl,mc13xxx-uses-touch;
leds {
#address-cells = <1>;
#size-cells = <0>;
led-control = <0x000 0x000 0x0e0 0x000>;
led_red@3 {
reg = <3>;
label = "mc13892:led_red";
leds,default-trigger = "none";
leds.default-state = "off";
};
led_green@4 {
reg = <4>;
label = "mc13892:led_green";
leds,default-trigger = "none";
leds.default-state = "off";
};
led_blue@5 {
reg = <5>;
label = "mc13892:led_blue";
leds,default-trigger = "heartbeat";
};
};
regulators {
sw1_reg: sw1 {
regulator-min-microvolt = <600000>;
regulator-max-microvolt = <1375000>;
regulator-boot-on;
regulator-always-on;
regulator-state-mem {
regulator-suspend-max-microvolt = <850000>;
regulator-mode = <2>;
regulator-on-in-suspend;
};
};
};
};
};
&iomuxc {
pinctrl_cspi: cspigrp {
fsl,pins = <
MX50_PAD_CSPI_SCLK__CSPI_SCLK 0x00
MX50_PAD_CSPI_MISO__CSPI_MISO 0x00
MX50_PAD_CSPI_MOSI__CSPI_MOSI 0x00
MX50_PAD_CSPI_SS0__GPIO4_11 0xc4
MX50_PAD_ECSPI1_MOSI__GPIO4_13 0x84
>;
};
pinctrl_pmic: pmicgrp {
fsl,pins = <
MX50_PAD_EIM_DA8__GPIO1_8 0xe5 /* IRQ */
>;
};
pinctrl_i2c1: i2c1grp {
fsl,pins = <
MX50_PAD_I2C1_SCL__I2C1_SCL 0x400001fd
MX50_PAD_I2C1_SDA__I2C1_SDA 0x400001fd
>;
};
pinctrl_i2c3: i2c3grp {
fsl,pins = <
MX50_PAD_I2C3_SCL__I2C3_SCL 0x400001fd
MX50_PAD_I2C3_SDA__I2C3_SDA 0x400001fd
>;
};
pinctrl_uart2: uart2grp {
fsl,pins = <
MX50_PAD_UART2_TXD__UART2_TXD_MUX 0x1e4
MX50_PAD_UART2_RXD__UART2_RXD_MUX 0x1e4
>;
};
pinctrl_sd1: sd1grp {
fsl,pins = <
MX50_PAD_SD1_CMD__ESDHC1_CMD 0x1e4
MX50_PAD_SD1_CLK__ESDHC1_CLK 0xd4
MX50_PAD_SD1_D0__ESDHC1_DAT0 0x1d4
MX50_PAD_SD1_D1__ESDHC1_DAT1 0x1d4
MX50_PAD_SD1_D2__ESDHC1_DAT2 0x1d4
MX50_PAD_SD1_D3__ESDHC1_DAT3 0x1d4
MX50_PAD_SD2_CD__GPIO5_17 0x0
>;
};
pinctrl_sd2: sd2grp {
fsl,pins = <
MX50_PAD_SD2_CMD__ESDHC2_CMD 0x1e4
MX50_PAD_SD2_CLK__ESDHC2_CLK 0xd4
MX50_PAD_SD2_D0__ESDHC2_DAT0 0x1d4
MX50_PAD_SD2_D1__ESDHC2_DAT1 0x1d4
MX50_PAD_SD2_D2__ESDHC2_DAT2 0x1d4
MX50_PAD_SD2_D3__ESDHC2_DAT3 0x1d4
>;
};
pinctrl_sd3: sd3grp {
fsl,pins = <
MX50_PAD_SD3_CMD__ESDHC3_CMD 0x1e4 /* mx50_nxt_io.c line 517 on */
MX50_PAD_SD3_CLK__ESDHC3_CLK 0xd4
MX50_PAD_SD3_D0__ESDHC3_DAT0 0x1d4
MX50_PAD_SD3_D1__ESDHC3_DAT1 0x1d4
MX50_PAD_SD3_D2__ESDHC3_DAT2 0x1d4
MX50_PAD_SD3_D3__ESDHC3_DAT3 0x1d4
>;
};
pinctrl_sd3_vmmc: sd3-vmmcgrp {
fsl,pins = <
MX50_PAD_ECSPI1_SCLK__GPIO4_12 0x0 /* mx50_ntx_io.c line 1922 */
>;
};
pinctrl_usbphy: usbphygrp {
fsl,pins = <
MX50_PAD_ECSPI2_SS0__GPIO4_19 0x0
>;
};
pinctrl_ts: tsgrp {
fsl,pins = <
MX50_PAD_SD2_D7__GPIO5_15 0x0 /* mx50_ntx_io.c line 1779 */
MX50_PAD_SD3_D6__GPIO5_26 0x0 /* mx50_ntx_io.c line 1786 */
>;
};
};
#include <stdint.h>
#include <stdio.h>
static const uint8_t data[] = {0x48, 0x57, 0x20, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x47, 0x20, 0x76, 0x31, 0x2E, 0x33, 0x00, 0x23, 0x15, 0x04, 0x00, 0x00, 0x07, 0x00, 0x00, 0x08, 0x04, 0x06, 0x06, 0x00, 0x00, 0x09, 0x00, 0x00, 0x02, 0x00, 0x02, 0x02, 0x02, 0x01, 0x68, 0x02, 0xE4, 0x00, 0x00, 0x02, 0x01, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
typedef struct tagNTXHWCFG_HDR {
char cMagicNameA[10];// should be "HW CONFIG "
char cVersionNameA[5];// should be "vx.x"
unsigned char bHWConfigSize;// v0.1=19 .
} NTXHWCFG_HDR ;
typedef struct tagNTXHWCFG_VAL {
unsigned char bPCB;//
unsigned char bKeyPad;// key pad type .
unsigned char bAudioCodec;//
unsigned char bAudioAmp;//
unsigned char bWifi;//
unsigned char bBT;//
unsigned char bMobile;//
unsigned char bTouchCtrl;//touch controller .
unsigned char bTouchType;//touch controller .
unsigned char bDisplayCtrl;//
unsigned char bDisplayPanel;//
unsigned char bRSensor;//
unsigned char bMicroP;//
unsigned char bCustomer;//
unsigned char bBattery;//
unsigned char bLed;//
unsigned char bRamSize;// ram size
unsigned char bIFlash; // internal flash type .
unsigned char bExternalMem;// external sd type .
unsigned char bRootFsType;// root fs type .
unsigned char bSysPartType;// system partition type .
unsigned char bProgressXHiByte; // Progress bar position while boot ,X
unsigned char bProgressXLoByte; // ,X
unsigned char bProgressYHiByte; // ,Y
unsigned char bProgressYLoByte; // ,Y
unsigned char bProgressCnts; // Cnt .
unsigned char bContentType; // Book content type .
unsigned char bCPU; //main cpu type .
unsigned char bUIStyle; // UI style .
unsigned char bRamType; // Ram Type .
unsigned char bUIConfig; // UI config .
unsigned char bDisplayResolution;// Display resolution .
unsigned char bFrontLight;// Front Light .
unsigned char bCPUFreq;// CPU frequency .
unsigned char bHallSensor;// Hall Sensor Controller .
unsigned char bDisplayBusWidth;// Display BUS width/bits .
unsigned char bFrontLight_Flags;// Front Light Flags .
unsigned char bPCB_Flags;// PCB Flags .
} NTXHWCFG_VAL ;
typedef struct tagNTX_HWCONFG{
NTXHWCFG_HDR m_hdr;
NTXHWCFG_VAL m_val;
unsigned char m_bReserveA[110-sizeof(NTXHWCFG_HDR)-sizeof(NTXHWCFG_VAL)];
} NTX_HWCONFIG;
int main() {
const NTX_HWCONFIG *config = (const NTX_HWCONFIG *)data;
// Print all fields of the NTX_HWCONFIG structure
printf("cMagicNameA: %.*s\n", (int)sizeof(config->m_hdr.cMagicNameA), config->m_hdr.cMagicNameA);
printf("cVersionNameA: %.*s\n", (int)sizeof(config->m_hdr.cVersionNameA), config->m_hdr.cVersionNameA);
printf("bHWConfigSize: %d\n", config->m_hdr.bHWConfigSize);
printf("bPCB: %d\n", config->m_val.bPCB);
printf("bKeyPad: %d\n", config->m_val.bKeyPad);
printf("bAudioCodec: %d\n", config->m_val.bAudioCodec);
printf("bAudioAmp: %d\n", config->m_val.bAudioAmp);
printf("bWifi: %d\n", config->m_val.bWifi);
printf("bBT: %d\n", config->m_val.bBT);
printf("bMobile: %d\n", config->m_val.bMobile);
printf("bTouchCtrl: %d\n", config->m_val.bTouchCtrl);
printf("bTouchType: %d\n", config->m_val.bTouchType);
printf("bDisplayCtrl: %d\n", config->m_val.bDisplayCtrl);
printf("bDisplayPanel: %d\n", config->m_val.bDisplayPanel);
printf("bRSensor: %d\n", config->m_val.bRSensor);
printf("bMicroP: %d\n", config->m_val.bMicroP);
printf("bCustomer: %d\n", config->m_val.bCustomer);
printf("bBattery: %d\n", config->m_val.bBattery);
printf("bLed: %d\n", config->m_val.bLed);
printf("bRamSize: %d\n", config->m_val.bRamSize);
printf("bIFlash: %d\n", config->m_val.bIFlash);
printf("bExternalMem: %d\n", config->m_val.bExternalMem);
printf("bRootFsType: %d\n", config->m_val.bRootFsType);
printf("bSysPartType: %d\n", config->m_val.bSysPartType);
printf("bProgressXHiByte: %d\n", config->m_val.bProgressXHiByte);
printf("bProgressXLoByte: %d\n", config->m_val.bProgressXLoByte);
printf("bProgressYHiByte: %d\n", config->m_val.bProgressYHiByte);
printf("bProgressYLoByte: %d\n", config->m_val.bProgressYLoByte);
printf("bProgressCnts: %d\n", config->m_val.bProgressCnts);
printf("bContentType: %d\n", config->m_val.bContentType);
printf("bCPU: %d\n", config->m_val.bCPU);
printf("bUIStyle: %d\n", config->m_val.bUIStyle);
printf("bRamType: %d\n", config->m_val.bRamType);
printf("bUIConfig: %d\n", config->m_val.bUIConfig);
printf("bDisplayResolution: %d\n", config->m_val.bDisplayResolution);
printf("bFrontLight: %d\n", config->m_val.bFrontLight);
printf("bCPUFreq: %d\n", config->m_val.bCPUFreq);
printf("bHallSensor: %d\n", config->m_val.bHallSensor);
printf("bDisplayBusWidth: %d\n", config->m_val.bDisplayBusWidth);
printf("bFrontLight_Flags: %d\n", config->m_val.bFrontLight_Flags);
printf("bPCB_Flags: %d\n", config->m_val.bPCB_Flags);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment