Connecting Buttons to Intel Edison

The Intel Edison has multiple GPIO pins, which can be connected to push buttons.

I use the gpio-keys Linux driver. Each button emits an event, which can be read in the same way as a keyboard event.

Schematic

Here is the schematic of my shield with the push buttons. The buttons are SW2, SW3, SW4, SW5.

Shiled with push buttons for Intel Edison
Shield with Push Buttons for Intel Edison

GPIO pullup code

As you can see, one side of a button is connected to the ground and another to a GPIO pin. I could use external pullup resistors, but the Intel Edison has built-in pullup resistors, which I activated instead. The current kernel code doesn’t have a function for setting a pullup resitor, so I had to write my own.

I added the function lnw_gpio_set_pull_alt into the Linux kernel file drivers/gpio/gpio-langwell.c

void lnw_gpio_set_pull_alt(unsigned gpio, int value, int pullup_value)
{
	struct lnw_gpio *lnw;
	u32 flis_offset;
	u32 flis_value;
	unsigned long flags;

	/* use this trick to get memio */
	lnw = irq_get_chip_data(gpio_to_irq(gpio));
	if (!lnw) {
		pr_err("langwell_gpio: can not find pin %d\n", gpio);
		return;
	}
	if (gpio < lnw->chip.base || gpio >= lnw->chip.base + lnw->chip.ngpio) {
		dev_err(lnw->chip.dev, "langwell_gpio: wrong pin %d to config alt\n", gpio);
		return;
	}
	gpio -= lnw->chip.base;

	if (lnw->type != TANGIER_GPIO) {
		return;
    }

	flis_offset = lnw->get_flis_offset(gpio);
	if (WARN(flis_offset == -EINVAL, "invalid pin %d\n", gpio))
		return -EINVAL;
	if (is_merr_i2c_flis(flis_offset))
		return;

	spin_lock_irqsave(&lnw->lock, flags);
	flis_value = get_flis_value(flis_offset);
	if (value) {
		flis_value |= PULLUP_ENABLE;
		flis_value &= ~PULLDOWN_ENABLE;
	} else {
		flis_value |= PULLDOWN_ENABLE;
		flis_value &= ~PULLUP_ENABLE;
	}
	//flis_value |= PUPD_VAL_50K;
    flis_value |= pullup_value;

	set_flis_value(flis_value, flis_offset);
	spin_unlock_irqrestore(&lnw->lock, flags);
}
EXPORT_SYMBOL_GPL(lnw_gpio_set_pull_alt);

Also added its declaration and the constants for the pullup values into the Linux kernel file include/linux/lnw_gpio.h

#define PUPD_VAL_2K	(0 << 4)
#define PUPD_VAL_20K	(1 << 4)
#define PUPD_VAL_50K	(2 << 4)
#define PUPD_VAL_910	(3 << 4)
void lnw_gpio_set_pull_alt(unsigned gpio, int value, int pullup_value);

I added the definitions for my 4 push buttons into the Linux kernel file arch/x86/platform/intel-mid/device_libs/platform_gpio_keys.c
The buttons are for GPIO12, GPIO13, GPIO182, GPIO183.
Each button configured as a general purpose pin by using the multiplexor code: lnw_gpio_set_alt(gb[i].gpio, LNW_GPIO);
The pullup value is set to 50K using the previously written function: lnw_gpio_set_pull_alt(gb[i].gpio, 1, PUPD_VAL_50K);

/*
 * platform_gpio_keys.c: gpio_keys platform data initilization file
 *
 * (C) Copyright 2008 Intel Corporation
 * Author:
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; version 2
 * of the License.
 */

#include <linux/input.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/gpio.h>
#include <linux/gpio_keys.h>
#include <linux/platform_device.h>
#include <asm/intel-mid.h>
#include "platform_gpio_keys.h"
#include <linux/lnw_gpio.h>

/*
 * we will search these buttons in SFI GPIO table (by name)
 * and register them dynamically. Please add all possible
 * buttons here, we will shrink them if no GPIO found.
 */
static struct gpio_keys_button gpio_button[] = {
        {
                .code = KEY_POWER,
                .gpio = -1, /* GPIO number */
                .active_low = 1,
                .desc = "power_btn",/*Button description*/
                .type = EV_KEY,
                .wakeup = 0,
                .debounce_interval = 3000,
        },
        {
                .code = KEY_UP,
                .gpio = 182,
                .active_low = 1,
                .desc = "up_btn",
                .type = EV_KEY,
                .wakeup = 0,
                .debounce_interval = 100,
        },
        {
                .code = KEY_DOWN,
                .gpio = 12,
                .active_low = 1,
                .desc = "down_btn",
                .type = EV_KEY,
                .wakeup = 0,
                .debounce_interval = 100,
        },
        {
                .code = KEY_ESC,
                .gpio = 13,
                .active_low = 1,
                .desc = "back_btn",
                .type = EV_KEY,
                .wakeup = 0,
                .debounce_interval = 100,
        },
        {
                .code = KEY_ENTER,
                .gpio = 183,
                .active_low = 1,
                .desc = "down_btn",
                .type = EV_KEY,
                .wakeup = 0,
                .debounce_interval = 100,
        },
};

static struct gpio_keys_platform_data gpio_keys = {
	.buttons	= gpio_button,
	.rep		= 1,
	.nbuttons	= -1, /* will fill it after search */
};

static struct platform_device pb_device = {
	.name		= DEVICE_NAME,
	.id		= -1,
	.dev		= {
		.platform_data	= &gpio_keys,
	},
};

/*
 * Shrink the non-existent buttons, register the gpio button
 * device if there is some
 */
static int __init pb_keys_init(void)
{
	struct gpio_keys_button *gb = gpio_button;
	int i, num, good = 0;

	num = sizeof(gpio_button) / sizeof(struct gpio_keys_button);
	for (i = 0; i < num; i++) {
		pr_info("info[%2d]: name = %s, gpio = %d\n",
			 i, gb[i].desc, gb[i].gpio);
		if (gb[i].gpio == -1)
			continue;

                if (gb[i].gpio > 0) {
                    lnw_gpio_set_alt(gb[i].gpio, LNW_GPIO);
                    lnw_gpio_set_pull_alt(gb[i].gpio, 1, PUPD_VAL_50K);
                }

		if (i != good)
			gb[good] = gb[i];
		good++;
	}

	if (good) {
		gpio_keys.nbuttons = good;
		return platform_device_register(&pb_device);
	}
	return 0;
}
late_initcall(pb_keys_init);
Testing Buttons

When the new Linux kernel was recompiled and flashed to the Intel Edison, I could verify the buttons.

I checked the files in the directory for the GPIO12: /sys/kernel/debug/gpio_debug/gpio12
current_pinmux was mode0
current_pullmode was pullup
current_pullstrength was 50k

I ran the command and tried to push on the buttons. I received some symbols. It meant that the buttons worked.

cat /dev/input/event0

I copied the file evtest.c from http://elinux.org/images/9/93/Evtest.c and compiled it on my Edison.

gcc evtest.c -o evtest

I ran the program evtest and got expected results.

# ./evtest /dev/input/event0
Input driver version is 1.0.1
Input device ID: bus 0x19 vendor 0x1 product 0x1 version 0x100
Input device name: "gpio-keys"
Supported events:
  Event type 0 (Sync)
  Event type 1 (Key)
    Event code 1 (Esc)
    Event code 28 (Enter)
    Event code 103 (Up)
    Event code 108 (Down)
  Event type 20 (Repeat)
Testing ... (interrupt to exit)
Event: time 1465107735.152126, type 1 (Key), code 108 (Down), value 1
Event: time 1465107735.152126, -------------- Report Sync ------------
Event: time 1465107735.344576, type 1 (Key), code 108 (Down), value 0
Event: time 1465107735.344576, -------------- Report Sync ------------
Event: time 1465107743.087594, type 1 (Key), code 103 (Up), value 1
Event: time 1465107743.087594, -------------- Report Sync ------------
Event: time 1465107743.332727, type 1 (Key), code 103 (Up), value 0
Event: time 1465107743.332727, -------------- Report Sync ------------
Event: time 1465107745.988003, type 1 (Key), code 28 (Enter), value 1
Event: time 1465107745.988003, -------------- Report Sync ------------
Event: time 1465107746.187692, type 1 (Key), code 28 (Enter), value 0
Event: time 1465107746.187692, -------------- Report Sync ------------
Event: time 1465107748.336925, type 1 (Key), code 1 (Esc), value 1
Event: time 1465107748.336925, -------------- Report Sync ------------
Event: time 1465107748.552085, type 1 (Key), code 1 (Esc), value 0
Event: time 1465107748.552085, -------------- Report Sync ------------

I may change the event key codes for the buttons. I can get the codes from the same file http://elinux.org/images/9/93/Evtest.c

Accessing /dev/input/event0 as a regular user

By default, the device /dev/input/event0 is accessible only by root.

I created the file /etc/udev/rules.d/input.rules
I chose the group “video” as I will use a user who should be able to access both the video device and the input device.

KERNEL=="event*", NAME="input/%k", MODE="660", GROUP="video"

I also created the file with the same contents poky/meta/recipes-core/systemd/systemd/input.rules
and added it into the recipe poky/meta/recipes-core/systemd/systemd_216.bb

SRC_URI = "git://anongit.freedesktop.org/systemd/systemd;branch=master;protocol=git \
           file://binfmt-install.patch \
           file://systemd-pam-configure-check-uclibc.patch \
           file://systemd-pam-fix-execvpe.patch \
           file://systemd-pam-fix-fallocate.patch \
           file://systemd-pam-fix-mkostemp.patch \
           file://optional_secure_getenv.patch \
           file://uclibc-sysinfo_h.patch \
           file://uclibc-get-physmem.patch \
           file://0001-add-support-for-executing-scripts-under-etc-rcS.d.patch \
           file://0001-missing.h-add-fake-__NR_memfd_create-for-MIPS.patch \
           file://0001-Make-root-s-home-directory-configurable.patch \
           file://0001-systemd-user-avoid-using-system-auth.patch \
           file://0001-journal-Fix-navigating-backwards-missing-entries.patch \
           file://0001-tmpfiles-make-resolv.conf-entry-conditional-on-resol.patch \
           file://0001-build-sys-do-not-install-tmpfiles-and-sysusers-files.patch \
           file://0001-build-sys-configure-the-list-of-system-users-files-a.patch \
           file://touchscreen.rules \
           file://input.rules \
           file://00-create-volatile.conf \
           file://init \
           file://run-ptest \
          "

2 thoughts on “Connecting Buttons to Intel Edison

  1. Just an example of pin control configuration (this one enable PWM output, you may adjust to your needs):

    #include
    #include
    #include

    static unsigned long pwm_config[] = {
    PIN_CONF_PACKED(PIN_CONFIG_BIAS_DISABLE, 0),
    };

    static struct pinctrl_map pwm_mapping[] __initdata = {
    PIN_MAP_MUX_GROUP_DEFAULT(“0000:00:17.0″, “pinctrl-merrifield”, “pwm0_grp”, “pwm0″),
    PIN_MAP_MUX_GROUP_DEFAULT(“0000:00:17.0″, “pinctrl-merrifield”, “pwm1_grp”, “pwm1″),
    PIN_MAP_MUX_GROUP_DEFAULT(“0000:00:17.0″, “pinctrl-merrifield”, “pwm2_grp”, “pwm2″),
    PIN_MAP_MUX_GROUP_DEFAULT(“0000:00:17.0″, “pinctrl-merrifield”, “pwm3_grp”, “pwm3″),
    PIN_MAP_CONFIGS_PIN_DEFAULT(“0000:00:17.0″, “pinctrl-merrifield”, “GP12_PWM0″, pwm_config),
    PIN_MAP_CONFIGS_PIN_DEFAULT(“0000:00:17.0″, “pinctrl-merrifield”, “GP13_PWM1″, pwm_config),
    PIN_MAP_CONFIGS_PIN_DEFAULT(“0000:00:17.0″, “pinctrl-merrifield”, “GP182_PWM2″, pwm_config),
    PIN_MAP_CONFIGS_PIN_DEFAULT(“0000:00:17.0″, “pinctrl-merrifield”, “GP183_PWM3″, pwm_config),
    };

    static int __init mrfld_pwm_init(void)
    {
    return pinctrl_register_mappings(pwm_mapping, ARRAY_SIZE(pwm_mapping));
    }

Leave a Reply