[U-Boot] i.MX6: mx6qsabrelite: Add keypress support

Submitted by Eric Nelson on March 3, 2012, midnight

Details

Message ID 1330732824-15345-1-git-send-email-eric.nelson@boundarydevices.com
State Rejected
Headers show

Commit Message

Eric Nelson March 3, 2012, midnight
This patch adds support for the GPIO keyboard used on MX6Q SabreLite.

This is generally used for invoking Android "recovery mode" in
response to a long press of volume key down during boot.

This can be tested by a boot script like so:
    if keypress voldown && sleep 1 && keypress voldown ; then
          echo "do recovery thing" ;
    fi

Key values can be seen by issuing keypress with no arguments:

	MX6QSABRELITE U-Boot > keypress
	keys: !menu	!back	!search	!home	!volup	!voldown
---
 board/freescale/mx6qsabrelite/mx6qsabrelite.c |   76 +++++++++++++++++++++++++
 1 files changed, 76 insertions(+), 0 deletions(-)

Comments

Eric Nelson March 3, 2012, 12:11 a.m.
On 03/02/2012 05:00 PM, Eric Nelson wrote:
> This patch adds support for the GPIO keyboard used on MX6Q SabreLite.
>
> This is generally used for invoking Android "recovery mode" in
> response to a long press of volume key down during boot.
>
> This can be tested by a boot script like so:
>      if keypress voldown&&  sleep 1&&  keypress voldown ; then
>            echo "do recovery thing" ;
>      fi
>
> Key values can be seen by issuing keypress with no arguments:
>
> 	MX6QSABRELITE U-Boot>  keypress
> 	keys: !menu	!back	!search	!home	!volup	!voldown
> ---
>   board/freescale/mx6qsabrelite/mx6qsabrelite.c |   76 +++++++++++++++++++++++++
>   1 files changed, 76 insertions(+), 0 deletions(-)
>
 > <snip>

I didn't want to litter the commit message with a lot of extraneous
discussion, but it appears that Android recovery mode can be invoked
either by Android itself or by a user pressing keys.

When Android wants to invoke recovery mode, it creates a special
"recovery" file and then re-boots.

The Freescale U-Boot release accomplishes this by having special
code to detect the keypress or the presence of the magic file.

http://opensource.freescale.com/git?p=imx/uboot-imx.git;a=blob;f=board/freescale/common/recovery.c;h=16e0be479ba543a8ceb865c3c2eee55379186bda;hb=imx_v2009.08_11.11.01

http://opensource.freescale.com/git?p=imx/uboot-imx.git;a=blob;f=board/freescale/mx53_loco/mx53_loco.c;h=fda52dc4abff6482d6cb102002529a3f2edd3bbb;hb=imx_v2009.08_11.11.01#l733

Since U-Boot can test files using the hush parser, it seems cleaner to just
enable keyboard detection and allow express the boot flow in boot commands.

I looked for, but didn't find precedent for testing keys.

Please advise if there's a more standard way to accomplish keypress
detection.

Regards,


Eric
Marek Vasut March 3, 2012, 2:18 a.m.
> This patch adds support for the GPIO keyboard used on MX6Q SabreLite.
> 
> This is generally used for invoking Android "recovery mode" in
> response to a long press of volume key down during boot.
> 
> This can be tested by a boot script like so:
>     if keypress voldown && sleep 1 && keypress voldown ; then
>           echo "do recovery thing" ;
>     fi
> 
> Key values can be seen by issuing keypress with no arguments:
> 
> 	MX6QSABRELITE U-Boot > keypress
> 	keys: !menu	!back	!search	!home	!volup	!voldown
> ---
>  board/freescale/mx6qsabrelite/mx6qsabrelite.c |   76
> +++++++++++++++++++++++++ 1 files changed, 76 insertions(+), 0
> deletions(-)
> 
> diff --git a/board/freescale/mx6qsabrelite/mx6qsabrelite.c
> b/board/freescale/mx6qsabrelite/mx6qsabrelite.c index e0ba6a4..0d45615
> 100644
> --- a/board/freescale/mx6qsabrelite/mx6qsabrelite.c
> +++ b/board/freescale/mx6qsabrelite/mx6qsabrelite.c
> @@ -50,6 +50,10 @@ DECLARE_GLOBAL_DATA_PTR;
>  	PAD_CTL_PUS_100K_DOWN | PAD_CTL_SPEED_MED |		\
>  	PAD_CTL_DSE_40ohm     | PAD_CTL_SRE_FAST)
> 
> +#define BUTTON_PAD_CTRL (PAD_CTL_PKE | PAD_CTL_PUE |		\
> +	PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_MED   |		\
> +	PAD_CTL_DSE_40ohm   | PAD_CTL_HYS)
> +
>  int dram_init(void)
>  {
>         gd->ram_size = get_ram_size((void *)PHYS_SDRAM, PHYS_SDRAM_SIZE);
> @@ -122,6 +126,15 @@ iomux_v3_cfg_t enet_pads2[] = {
>  	MX6Q_PAD_RGMII_RX_CTL__RGMII_RX_CTL	| MUX_PAD_CTRL(ENET_PAD_CTRL),
>  };
> 
> +static iomux_v3_cfg_t const button_pads[] = {
> +	MX6Q_PAD_NANDF_D1__GPIO_2_1	| MUX_PAD_CTRL(BUTTON_PAD_CTRL), /* J14 
-
> Menu Button */ +	MX6Q_PAD_NANDF_D2__GPIO_2_2	|
> MUX_PAD_CTRL(BUTTON_PAD_CTRL), /* J14 - Back Button */
> +	MX6Q_PAD_NANDF_D3__GPIO_2_3	| MUX_PAD_CTRL(BUTTON_PAD_CTRL), /* J14 
-
> Search Button */ +	MX6Q_PAD_NANDF_D4__GPIO_2_4	|
> MUX_PAD_CTRL(BUTTON_PAD_CTRL), /* J14 - Home Button */
> +	MX6Q_PAD_GPIO_19__GPIO_4_5	| MUX_PAD_CTRL(BUTTON_PAD_CTRL), /* J14 
-
> Volume Down */ +	MX6Q_PAD_GPIO_18__GPIO_7_13	|
> MUX_PAD_CTRL(BUTTON_PAD_CTRL), /* J14 - Volume Up */ +};
> +
>  static void setup_iomux_enet(void)
>  {
>  	gpio_direction_output(87, 0);  /* GPIO 3-23 */
> @@ -323,10 +336,18 @@ int setup_sata(void)
>  }
>  #endif
> 
> +static void setup_buttons(void)
> +{
> +	imx_iomux_v3_setup_multiple_pads(button_pads,
> +					 ARRAY_SIZE(button_pads));
> +}
> +
>  int board_early_init_f(void)
>  {
>         setup_iomux_uart();
> 
> +       setup_buttons();
> +
>  #ifdef CONFIG_CMD_SATA
>  	setup_sata();
>  #endif
> @@ -350,3 +371,58 @@ int checkboard(void)
> 
>         return 0;
>  }
> +
> +struct button_key {
> +	char const	*name;
> +	unsigned	gpnum;
> +};
> +
> +static struct button_key const buttons[] = {
> +	{"menu",	GPIO_NUMBER(2, 1)},
> +	{"back",	GPIO_NUMBER(2, 2)},
> +	{"search",	GPIO_NUMBER(2, 3)},
> +	{"home",	GPIO_NUMBER(2, 4)},
> +	{"voldown",	GPIO_NUMBER(4, 5)},
> +	{"volup",	GPIO_NUMBER(7, 13)},
> +};
> +
> +static int keypress(cmd_tbl_t *cmdtp, int flag, int argc, char * const
> argv[]) +{
> +	if (1 < argc) {
> +		int arg;
> +		int pressed = 1 ;
> +		for (arg=1; arg<argc; arg++) {
> +			char const *keyname=argv[arg];
> +			int i;
> +			for (i=0; pressed && (i < ARRAY_SIZE(buttons)); i++) {
> +				if (0 == strcmp(buttons[i].name,keyname)) {
> +					pressed = pressed && (0 == 
gpio_get_value(buttons[i].gpnum));
> +					break;
> +				}
> +			}
> +			if (ARRAY_SIZE(buttons) == i) {
> +				printf ("unrecognized key %s\n", keyname);
> +				pressed = 0;
> +				break;
> +			}
> +		}
> +		return (0 == pressed);
> +	} else {
> +		int i;
> +		printf ("keys: ");
> +		for (i=0; i<ARRAY_SIZE(buttons); i++) {
> +			if (0 != gpio_get_value(buttons[i].gpnum))
> +				printf("!");
> +			printf("%s\t",buttons[i].name);
> +		}
> +		printf("\n");
> +		return 0 ;
> +	}
> +}
> +
> +U_BOOT_CMD(
> +	keypress, CONFIG_SYS_MAXARGS, 1, keypress,
> +	"Display or test keypresses",
> +	"    keypress		- show key(s) pressed\n"
> +	"    keypress name	- test key name (return 0 if pressed)\n"
> +);

Why not make it an STDIN device as any other keyboard?

M
Wolfgang Denk March 3, 2012, 9:15 a.m.
Dear Eric Nelson,

In message <1330732824-15345-1-git-send-email-eric.nelson@boundarydevices.com> you wrote:
> This patch adds support for the GPIO keyboard used on MX6Q SabreLite.
> 
> This is generally used for invoking Android "recovery mode" in
> response to a long press of volume key down during boot.
> 
> This can be tested by a boot script like so:
>     if keypress voldown && sleep 1 && keypress voldown ; then
>           echo "do recovery thing" ;
>     fi
> 
> Key values can be seen by issuing keypress with no arguments:

I don't like introducing yet another way to handle key presses and
create menu like interfaces from this.

We already have two of these:

- We have the powerful and flexible method to map key preesses to
  envrionment variables which can in turn hold commands (variables
  "magic_keys" and "key_magic*") as used for example on the enbw_cmc,
  lwmon5, hmi1001, mucmc52, pcs440ep, r360mpi and mucmc52 boards.

- We have the menu system as implemented by common/menu.c etc.


Please use either of these, but don't invent a new one.  Thanks.

Best regards,

Wolfgang Denk
Eric Nelson March 3, 2012, 3:30 p.m.
On 03/02/2012 07:18 PM, Marek Vasut wrote:
>> This patch adds support for the GPIO keyboard used on MX6Q SabreLite.
>>
>> This is generally used for invoking Android "recovery mode" in
>> response to a long press of volume key down during boot.
>>
>> This can be tested by a boot script like so:
>>      if keypress voldown&&  sleep 1&&  keypress voldown ; then
>>            echo "do recovery thing" ;
>>      fi
>>
>> Key values can be seen by issuing keypress with no arguments:
>>
>> 	MX6QSABRELITE U-Boot>  keypress
>> 	keys: !menu	!back	!search	!home	!volup	!voldown
>> ---
>>   board/freescale/mx6qsabrelite/mx6qsabrelite.c |   76
>> +++++++++++++++++++++++++ 1 files changed, 76 insertions(+), 0
>> deletions(-)
>>
>> diff --git a/board/freescale/mx6qsabrelite/mx6qsabrelite.c
>> b/board/freescale/mx6qsabrelite/mx6qsabrelite.c index e0ba6a4..0d45615
>> 100644
>> --- a/board/freescale/mx6qsabrelite/mx6qsabrelite.c
>> +++ b/board/freescale/mx6qsabrelite/mx6qsabrelite.c
>> @@ -50,6 +50,10 @@ DECLARE_GLOBAL_DATA_PTR;
>>   	PAD_CTL_PUS_100K_DOWN | PAD_CTL_SPEED_MED |		\
>>   	PAD_CTL_DSE_40ohm     | PAD_CTL_SRE_FAST)
>>
>> +#define BUTTON_PAD_CTRL (PAD_CTL_PKE | PAD_CTL_PUE |		\
>> +	PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_MED   |		\
>> +	PAD_CTL_DSE_40ohm   | PAD_CTL_HYS)
>> +
>>   int dram_init(void)
>>   {
>>          gd->ram_size = get_ram_size((void *)PHYS_SDRAM, PHYS_SDRAM_SIZE);
>> @@ -122,6 +126,15 @@ iomux_v3_cfg_t enet_pads2[] = {
>>   	MX6Q_PAD_RGMII_RX_CTL__RGMII_RX_CTL	| MUX_PAD_CTRL(ENET_PAD_CTRL),
>>   };
>>
>> +static iomux_v3_cfg_t const button_pads[] = {
>> +	MX6Q_PAD_NANDF_D1__GPIO_2_1	| MUX_PAD_CTRL(BUTTON_PAD_CTRL), /* J14
> -
>> Menu Button */ +	MX6Q_PAD_NANDF_D2__GPIO_2_2	|
>> MUX_PAD_CTRL(BUTTON_PAD_CTRL), /* J14 - Back Button */
>> +	MX6Q_PAD_NANDF_D3__GPIO_2_3	| MUX_PAD_CTRL(BUTTON_PAD_CTRL), /* J14
> -
>> Search Button */ +	MX6Q_PAD_NANDF_D4__GPIO_2_4	|
>> MUX_PAD_CTRL(BUTTON_PAD_CTRL), /* J14 - Home Button */
>> +	MX6Q_PAD_GPIO_19__GPIO_4_5	| MUX_PAD_CTRL(BUTTON_PAD_CTRL), /* J14
> -
>> Volume Down */ +	MX6Q_PAD_GPIO_18__GPIO_7_13	|
>> MUX_PAD_CTRL(BUTTON_PAD_CTRL), /* J14 - Volume Up */ +};
>> +
>>   static void setup_iomux_enet(void)
>>   {
>>   	gpio_direction_output(87, 0);  /* GPIO 3-23 */
>> @@ -323,10 +336,18 @@ int setup_sata(void)
>>   }
>>   #endif
>>
>> +static void setup_buttons(void)
>> +{
>> +	imx_iomux_v3_setup_multiple_pads(button_pads,
>> +					 ARRAY_SIZE(button_pads));
>> +}
>> +
>>   int board_early_init_f(void)
>>   {
>>          setup_iomux_uart();
>>
>> +       setup_buttons();
>> +
>>   #ifdef CONFIG_CMD_SATA
>>   	setup_sata();
>>   #endif
>> @@ -350,3 +371,58 @@ int checkboard(void)
>>
>>          return 0;
>>   }
>> +
>> +struct button_key {
>> +	char const	*name;
>> +	unsigned	gpnum;
>> +};
>> +
>> +static struct button_key const buttons[] = {
>> +	{"menu",	GPIO_NUMBER(2, 1)},
>> +	{"back",	GPIO_NUMBER(2, 2)},
>> +	{"search",	GPIO_NUMBER(2, 3)},
>> +	{"home",	GPIO_NUMBER(2, 4)},
>> +	{"voldown",	GPIO_NUMBER(4, 5)},
>> +	{"volup",	GPIO_NUMBER(7, 13)},
>> +};
>> +
>> +static int keypress(cmd_tbl_t *cmdtp, int flag, int argc, char * const
>> argv[]) +{
>> +	if (1<  argc) {
>> +		int arg;
>> +		int pressed = 1 ;
>> +		for (arg=1; arg<argc; arg++) {
>> +			char const *keyname=argv[arg];
>> +			int i;
>> +			for (i=0; pressed&&  (i<  ARRAY_SIZE(buttons)); i++) {
>> +				if (0 == strcmp(buttons[i].name,keyname)) {
>> +					pressed = pressed&&  (0 ==
> gpio_get_value(buttons[i].gpnum));
>> +					break;
>> +				}
>> +			}
>> +			if (ARRAY_SIZE(buttons) == i) {
>> +				printf ("unrecognized key %s\n", keyname);
>> +				pressed = 0;
>> +				break;
>> +			}
>> +		}
>> +		return (0 == pressed);
>> +	} else {
>> +		int i;
>> +		printf ("keys: ");
>> +		for (i=0; i<ARRAY_SIZE(buttons); i++) {
>> +			if (0 != gpio_get_value(buttons[i].gpnum))
>> +				printf("!");
>> +			printf("%s\t",buttons[i].name);
>> +		}
>> +		printf("\n");
>> +		return 0 ;
>> +	}
>> +}
>> +
>> +U_BOOT_CMD(
>> +	keypress, CONFIG_SYS_MAXARGS, 1, keypress,
>> +	"Display or test keypresses",
>> +	"    keypress		- show key(s) pressed\n"
>> +	"    keypress name	- test key name (return 0 if pressed)\n"
>> +);
>
> Why not make it an STDIN device as any other keyboard?
>
Is there a non-blocking read from stdin available to boot script?

How would we represent keys like "Menu", "Home", "Volume up" and "Volume down"?

Through ANSI escape sequences?

Please advise,


Eric
Eric Nelson March 3, 2012, 3:35 p.m.
On 03/03/2012 02:15 AM, Wolfgang Denk wrote:
> Dear Eric Nelson,
>
> In message<1330732824-15345-1-git-send-email-eric.nelson@boundarydevices.com>  you wrote:
>> This patch adds support for the GPIO keyboard used on MX6Q SabreLite.
>>
>> This is generally used for invoking Android "recovery mode" in
>> response to a long press of volume key down during boot.
>>
>> This can be tested by a boot script like so:
>>      if keypress voldown&&  sleep 1&&  keypress voldown ; then
>>            echo "do recovery thing" ;
>>      fi
>>
>> Key values can be seen by issuing keypress with no arguments:
>
> I don't like introducing yet another way to handle key presses and
> create menu like interfaces from this.
>
> We already have two of these:
>
> - We have the powerful and flexible method to map key preesses to
>    envrionment variables which can in turn hold commands (variables
>    "magic_keys" and "key_magic*") as used for example on the enbw_cmc,
>    lwmon5, hmi1001, mucmc52, pcs440ep, r360mpi and mucmc52 boards.
>
> - We have the menu system as implemented by common/menu.c etc.
>
> Please use either of these, but don't invent a new one.  Thanks.
>

Thanks for the pointers. I'll rework accordingly.

Grepping the sources rarely results in this kind of insight.

Regards,


Eric
Wolfgang Denk March 3, 2012, 3:48 p.m.
Dear Eric Nelson,

In message <4F52390C.4080108@boundarydevices.com> you wrote:
>
> > Why not make it an STDIN device as any other keyboard?
> >
> Is there a non-blocking read from stdin available to boot script?
> 
> How would we represent keys like "Menu", "Home", "Volume up" and "Volume down"?
> 
> Through ANSI escape sequences?

No.  You don't have to.  Mapping key presses to functions (bind them to
commands) is a different thing.  Keys could be "1", "2", "3" and "4",
and could be mapped to "run cmd_1", ... "run cmd_4" respectively.
Then the user can define what "cmd_1" etc. does.

Best regards,

Wolfgang Denk
Wolfgang Denk March 3, 2012, 3:50 p.m.
Dear Eric Nelson,

In message <4F523A24.8020406@boundarydevices.com> you wrote:
>
> Thanks for the pointers. I'll rework accordingly.

Thanks.

> Grepping the sources rarely results in this kind of insight.

I know.  I can find the stuff myself only because I have an idea what
to search for...

Best regards,

Wolfgang Denk
Eric Nelson March 3, 2012, 3:51 p.m.
On 03/03/2012 08:48 AM, Wolfgang Denk wrote:
> Dear Eric Nelson,
>
> In message<4F52390C.4080108@boundarydevices.com>  you wrote:
>>
>>> Why not make it an STDIN device as any other keyboard?
>>>
>> Is there a non-blocking read from stdin available to boot script?
>>
>> How would we represent keys like "Menu", "Home", "Volume up" and "Volume down"?
>>
>> Through ANSI escape sequences?
>
> No.  You don't have to.  Mapping key presses to functions (bind them to
> commands) is a different thing.  Keys could be "1", "2", "3" and "4",
> and could be mapped to "run cmd_1", ... "run cmd_4" respectively.
> Then the user can define what "cmd_1" etc. does.
>

That's perfect. All that's left is the details...

Thanks for your help.
Dirk Behme March 24, 2012, 7:13 a.m.
Hi Eric,

On 03.03.2012 16:51, Eric Nelson wrote:
> On 03/03/2012 08:48 AM, Wolfgang Denk wrote:
>> Dear Eric Nelson,
>>
>> In message<4F52390C.4080108@boundarydevices.com> you wrote:
>>>
>>>> Why not make it an STDIN device as any other keyboard?
>>>>
>>> Is there a non-blocking read from stdin available to boot script?
>>>
>>> How would we represent keys like "Menu", "Home", "Volume up" and
>>> "Volume down"?
>>>
>>> Through ANSI escape sequences?
>>
>> No. You don't have to. Mapping key presses to functions (bind them to
>> commands) is a different thing. Keys could be "1", "2", "3" and "4",
>> and could be mapped to "run cmd_1", ... "run cmd_4" respectively.
>> Then the user can define what "cmd_1" etc. does.
>>
>
> That's perfect. All that's left is the details...

Do you plan an update of this patch?

Many thanks,

Dirk
Eric Nelson March 24, 2012, 10:39 p.m.
On 03/24/2012 12:13 AM, Dirk Behme wrote:
> Hi Eric,
>
> On 03.03.2012 16:51, Eric Nelson wrote:
>> On 03/03/2012 08:48 AM, Wolfgang Denk wrote:
>>> Dear Eric Nelson,
>>>
>>> In message<4F52390C.4080108@boundarydevices.com> you wrote:
>>>>
>>>>> Why not make it an STDIN device as any other keyboard?
>>>>>
>>>> Is there a non-blocking read from stdin available to boot script?
>>>>
>>>> How would we represent keys like "Menu", "Home", "Volume up" and
>>>> "Volume down"?
>>>>
>>>> Through ANSI escape sequences?
>>>
>>> No. You don't have to. Mapping key presses to functions (bind them to
>>> commands) is a different thing. Keys could be "1", "2", "3" and "4",
>>> and could be mapped to "run cmd_1", ... "run cmd_4" respectively.
>>> Then the user can define what "cmd_1" etc. does.
>>>
>> That's perfect. All that's left is the details...
>
> Do you plan an update of this patch?
>

Yes I do. I'm just having trouble finding time these days.

And I seem to be buttonless (without a button board) at the moment.

I even have a fledgling README for keyboard support so the next person
can find out what to do with grep!

Regards,


Eric

Patch hide | download patch | download mbox

diff --git a/board/freescale/mx6qsabrelite/mx6qsabrelite.c b/board/freescale/mx6qsabrelite/mx6qsabrelite.c
index e0ba6a4..0d45615 100644
--- a/board/freescale/mx6qsabrelite/mx6qsabrelite.c
+++ b/board/freescale/mx6qsabrelite/mx6qsabrelite.c
@@ -50,6 +50,10 @@  DECLARE_GLOBAL_DATA_PTR;
 	PAD_CTL_PUS_100K_DOWN | PAD_CTL_SPEED_MED |		\
 	PAD_CTL_DSE_40ohm     | PAD_CTL_SRE_FAST)
 
+#define BUTTON_PAD_CTRL (PAD_CTL_PKE | PAD_CTL_PUE |		\
+	PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_MED   |		\
+	PAD_CTL_DSE_40ohm   | PAD_CTL_HYS)
+
 int dram_init(void)
 {
        gd->ram_size = get_ram_size((void *)PHYS_SDRAM, PHYS_SDRAM_SIZE);
@@ -122,6 +126,15 @@  iomux_v3_cfg_t enet_pads2[] = {
 	MX6Q_PAD_RGMII_RX_CTL__RGMII_RX_CTL	| MUX_PAD_CTRL(ENET_PAD_CTRL),
 };
 
+static iomux_v3_cfg_t const button_pads[] = {
+	MX6Q_PAD_NANDF_D1__GPIO_2_1	| MUX_PAD_CTRL(BUTTON_PAD_CTRL), /* J14 - Menu Button */
+	MX6Q_PAD_NANDF_D2__GPIO_2_2	| MUX_PAD_CTRL(BUTTON_PAD_CTRL), /* J14 - Back Button */
+	MX6Q_PAD_NANDF_D3__GPIO_2_3	| MUX_PAD_CTRL(BUTTON_PAD_CTRL), /* J14 - Search Button */
+	MX6Q_PAD_NANDF_D4__GPIO_2_4	| MUX_PAD_CTRL(BUTTON_PAD_CTRL), /* J14 - Home Button */
+	MX6Q_PAD_GPIO_19__GPIO_4_5	| MUX_PAD_CTRL(BUTTON_PAD_CTRL), /* J14 - Volume Down */
+	MX6Q_PAD_GPIO_18__GPIO_7_13	| MUX_PAD_CTRL(BUTTON_PAD_CTRL), /* J14 - Volume Up */
+};
+
 static void setup_iomux_enet(void)
 {
 	gpio_direction_output(87, 0);  /* GPIO 3-23 */
@@ -323,10 +336,18 @@  int setup_sata(void)
 }
 #endif
 
+static void setup_buttons(void)
+{
+	imx_iomux_v3_setup_multiple_pads(button_pads,
+					 ARRAY_SIZE(button_pads));
+}
+
 int board_early_init_f(void)
 {
        setup_iomux_uart();
 
+       setup_buttons();
+
 #ifdef CONFIG_CMD_SATA
 	setup_sata();
 #endif
@@ -350,3 +371,58 @@  int checkboard(void)
 
        return 0;
 }
+
+struct button_key {
+	char const	*name;
+	unsigned	gpnum;
+};
+
+static struct button_key const buttons[] = {
+	{"menu",	GPIO_NUMBER(2, 1)},
+	{"back",	GPIO_NUMBER(2, 2)},
+	{"search",	GPIO_NUMBER(2, 3)},
+	{"home",	GPIO_NUMBER(2, 4)},
+	{"voldown",	GPIO_NUMBER(4, 5)},
+	{"volup",	GPIO_NUMBER(7, 13)},
+};
+
+static int keypress(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	if (1 < argc) {
+		int arg;
+		int pressed = 1 ;
+		for (arg=1; arg<argc; arg++) {
+			char const *keyname=argv[arg];
+			int i;
+			for (i=0; pressed && (i < ARRAY_SIZE(buttons)); i++) {
+				if (0 == strcmp(buttons[i].name,keyname)) {
+					pressed = pressed && (0 == gpio_get_value(buttons[i].gpnum));
+					break;
+				}
+			}
+			if (ARRAY_SIZE(buttons) == i) {
+				printf ("unrecognized key %s\n", keyname);
+				pressed = 0;
+				break;
+			}
+		}
+		return (0 == pressed);
+	} else {
+		int i;
+		printf ("keys: ");
+		for (i=0; i<ARRAY_SIZE(buttons); i++) {
+			if (0 != gpio_get_value(buttons[i].gpnum))
+				printf("!");
+			printf("%s\t",buttons[i].name);
+		}
+		printf("\n");
+		return 0 ;
+	}
+}
+
+U_BOOT_CMD(
+	keypress, CONFIG_SYS_MAXARGS, 1, keypress,
+	"Display or test keypresses",
+	"    keypress		- show key(s) pressed\n"
+	"    keypress name	- test key name (return 0 if pressed)\n"
+);