This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Linux/AM572X: Keypad module issue

SDK: ti-processor-sdk-linux-am57xx-evm-03.02.00.05

there is some strange thing about the interrupt of keypad module. 

some times too slow to send the key release interrupt.

from attachment ZOC1708.LOG, you can see, using same click on keypad,but sometimes keypad interrupt comes within 100ms, sometime 1s.

dts.rar are dts files

omap4-keypad.c:the keypad driver base on the same name file from sdk.

anything is wrong?

should I provide the pin mux file or anything else?

ZOC1708.LOG

dts.rar

/*
 * OMAP4 Keypad Driver
 *
 * Copyright (C) 2010 Texas Instruments
 *
 * Author: Abraham Arce <x0066660@ti.com>
 * Initial Code: Syed Rafiuddin <rafiuddin.syed@ti.com>
 *
 * 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; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/input.h>
#include <linux/input/matrix_keypad.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>

/* OMAP4 registers */
#define OMAP4_KBD_REVISION		0x00
#define OMAP4_KBD_SYSCONFIG		0x10
#define OMAP4_KBD_SYSSTATUS		0x14
#define OMAP4_KBD_IRQSTATUS		0x18
#define OMAP4_KBD_IRQENABLE		0x1C
#define OMAP4_KBD_WAKEUPENABLE		0x20
#define OMAP4_KBD_PENDING		0x24
#define OMAP4_KBD_CTRL			0x28
#define OMAP4_KBD_DEBOUNCINGTIME	0x2C
#define OMAP4_KBD_LONGKEYTIME		0x30
#define OMAP4_KBD_TIMEOUT		0x34
#define OMAP4_KBD_STATEMACHINE		0x38
#define OMAP4_KBD_ROWINPUTS		0x3C
#define OMAP4_KBD_COLUMNOUTPUTS		0x40
#define OMAP4_KBD_FULLCODE31_0		0x44
#define OMAP4_KBD_FULLCODE63_32		0x48

#define OMAP5_KBD_IRQSTATUS_RAW		0x12
#define OMAP5_KBD_IRQCLEAR          0x20

/* OMAP4 bit definitions */
#define OMAP4_DEF_IRQENABLE_EVENTEN	BIT(0)
#if 0
#define OMAP4_DEF_IRQENABLE_LONGKEY	BIT(1)
#define OMAP4_DEF_WUP_EVENT_ENA		BIT(0)
#define OMAP4_DEF_WUP_LONG_KEY_ENA	BIT(1)
#else

#define OMAP4_DEF_IRQENABLE_LONGKEY	0
#define OMAP4_DEF_WUP_EVENT_ENA		0
#define OMAP4_DEF_WUP_LONG_KEY_ENA	0
#endif

#define OMAP4_DEF_CTRL_NOSOFTMODE	BIT(1)
#define OMAP4_DEF_CTRL_PTV_SHIFT	2

/* OMAP4 values */
#define OMAP4_VAL_IRQDISABLE		0x0
#define OMAP4_VAL_DEBOUNCINGTIME	0x7
#define OMAP4_VAL_PVT			    0x7
#define OMAP5_VAL_IRQDISABLE        0x7

enum {
	KBD_REVISION_OMAP4 = 0,
	KBD_REVISION_OMAP5,
};

#define MAX_ROWS_NUM 8

struct omap4_keypad {
	struct input_dev *input;

	void __iomem *base;
	bool irq_wake_enabled;
	unsigned int irq;

    unsigned int row_off;
	unsigned int rows;
	unsigned int cols;
	u32 reg_offset;
	u32 irqreg_offset;
	unsigned int row_shift;
	bool no_autorepeat;
	unsigned char key_state[MAX_ROWS_NUM];
	unsigned short *keymap;

    int res_start;
    int res_size;
    int irq_clear_reg_off;
    int irq_clear_reg_val;
};

static int kbd_readl(struct omap4_keypad *keypad_data, u32 offset)
{
	return __raw_readl(keypad_data->base +
				keypad_data->reg_offset + offset);
}

static void kbd_writel(struct omap4_keypad *keypad_data, u32 offset, u32 value)
{
	__raw_writel(value,
		     keypad_data->base + keypad_data->reg_offset + offset);
}

static int kbd_read_irqreg(struct omap4_keypad *keypad_data, u32 offset)
{
	int ret =  __raw_readl(keypad_data->base +
				           keypad_data->irqreg_offset + offset);
//    pr_warn("%s, addr:%p, value:%d\n", __func__, keypad_data->base + keypad_data->irqreg_offset + offset, ret);
    return ret;
}

static void kbd_write_irqreg(struct omap4_keypad *keypad_data,
			     u32 offset, u32 value)
{
//    pr_warn("%s, addr:%p, value:%d\n", __func__, keypad_data->base + keypad_data->irqreg_offset + offset, value);
	__raw_writel(value,
		     keypad_data->base + keypad_data->irqreg_offset + offset);
}


/* Interrupt handlers */
static irqreturn_t omap4_keypad_irq_handler(int irq, void *dev_id)
{
	struct omap4_keypad *keypad_data = dev_id;

    int ret = kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS);
    pr_warn("%s, irq read IRQSTATUS:%x\n", __func__, ret);
	if (ret) {
    	/* clear pending interrupts */
	    kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
		            	 kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS));

		/* Disable interrupts */
		kbd_write_irqreg(keypad_data, keypad_data->irq_clear_reg_off,
		                 keypad_data->irq_clear_reg_val);
        
		return IRQ_WAKE_THREAD;
	}

	return IRQ_NONE;
}

static irqreturn_t omap4_keypad_irq_thread_fn(int irq, void *dev_id)
{
	struct omap4_keypad *keypad_data = dev_id;
	struct input_dev *input_dev = keypad_data->input;
	unsigned char key_state[ARRAY_SIZE(keypad_data->key_state)];
	unsigned int col, row, code, changed;
    unsigned int row_off = keypad_data->row_off;
	u32 *new_state = (u32 *) key_state;
    bool b_key_change = false;

	*new_state = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0);
	*(new_state + 1) = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32);

    pr_warn("%s, new_state:%x, %x, row_off:%d\n", __func__, *new_state, *(new_state+1), row_off);

	for (row = row_off; row < row_off + keypad_data->rows; row++) {
		changed = key_state[row] ^ keypad_data->key_state[row];
		if (!changed)
			continue;

		for (col = 0; col < keypad_data->cols; col++) {
			if (changed & (1 << col)) {
				code = MATRIX_SCAN_CODE(row - row_off, col,
						keypad_data->row_shift);
				input_event(input_dev, EV_MSC, MSC_SCAN, code);
				input_report_key(input_dev,
						 keypad_data->keymap[code],
						 key_state[row] & (1 << col));
                b_key_change = true;
			}
		}
	}

    if (b_key_change == true)
    {
	    input_sync(input_dev);
    }

	memcpy(keypad_data->key_state, key_state,
		sizeof(keypad_data->key_state));

#if 0
	/* clear pending interrupts */
	kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
			 kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS));

#endif
	/* enable interrupts */
	kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE,
		OMAP4_DEF_IRQENABLE_EVENTEN |
				OMAP4_DEF_IRQENABLE_LONGKEY);
	return IRQ_HANDLED;
}

static int omap4_keypad_open(struct input_dev *input)
{
	struct omap4_keypad *keypad_data = input_get_drvdata(input);

	pm_runtime_get_sync(input->dev.parent);

	disable_irq(keypad_data->irq);

	kbd_writel(keypad_data, OMAP4_KBD_CTRL,
			OMAP4_DEF_CTRL_NOSOFTMODE |
			(OMAP4_VAL_PVT << OMAP4_DEF_CTRL_PTV_SHIFT));
	kbd_writel(keypad_data, OMAP4_KBD_DEBOUNCINGTIME,
			OMAP4_VAL_DEBOUNCINGTIME);
	/* clear pending interrupts */
	kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
			 kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS));
	kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE,
			OMAP4_DEF_IRQENABLE_EVENTEN |
				OMAP4_DEF_IRQENABLE_LONGKEY);
	kbd_writel(keypad_data, OMAP4_KBD_WAKEUPENABLE,
			OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA);

	enable_irq(keypad_data->irq);

	return 0;
}

static void omap4_keypad_close(struct input_dev *input)
{
	struct omap4_keypad *keypad_data = input_get_drvdata(input);

	disable_irq(keypad_data->irq);

	/* Disable interrupts */
	kbd_write_irqreg(keypad_data, keypad_data->irq_clear_reg_off,
			 keypad_data->irq_clear_reg_val);

	/* clear pending interrupts */
	kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
			 kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS));

	enable_irq(keypad_data->irq);

	pm_runtime_put_sync(input->dev.parent);
}

static int omap4_keypad_parse_dt(struct device *dev,
				 struct omap4_keypad *keypad_data)
{
	struct device_node *np = dev->of_node;
	int err;

	err = matrix_keypad_parse_of_params(dev, &keypad_data->rows,
					    &keypad_data->cols);
	if (err)
		return err;

	if (of_get_property(np, "linux,input-no-autorepeat", NULL))
		keypad_data->no_autorepeat = true;

	err = of_property_read_u32(np, "keypad,row-offset", &(keypad_data->row_off));
    if (err)
    {
        keypad_data->row_off = 0;
    }

    if (keypad_data->row_off >= MAX_ROWS_NUM)
    {
		dev_err(dev, "error keypad,row-offset exceed max row:%d\n", MAX_ROWS_NUM);
		return -EINVAL;
    }

    if (keypad_data->row_off + keypad_data->rows > MAX_ROWS_NUM)
    {
		dev_err(dev, "error keypad,row-offset and keypad,num-rows exceed max row:%d\n", MAX_ROWS_NUM);
		return -EINVAL;
    }

	return 0;
}

static int omap4_keypad_probe(struct platform_device *pdev)
{
	struct omap4_keypad *keypad_data;
	struct input_dev *input_dev;
	struct resource *res;
	unsigned int max_keys;
	int rev;
	int irq;
	int error;
	struct device_node *np = pdev->dev.of_node;
    int res_mem[2];

#if 0
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res) {
		dev_err(&pdev->dev, "no base address specified\n");
		return -EINVAL;
	}
#endif
    dev_warn(&pdev->dev, "init start\n");
	if (!np)
    {
		dev_err(&pdev->dev, "Device Tree node missing\n");
		return -EINVAL;
	}

    error = of_property_read_u32_array(np, "reg",
	                				 res_mem,
					            	 sizeof(res_mem) / sizeof(res_mem[0]));
	if (error < 0)
    {
		dev_err(&pdev->dev, "no base address specified\n");
    	return error;
    }

	irq = platform_get_irq(pdev, 0);
	if (!irq) {
		dev_err(&pdev->dev, "no keyboard irq assigned\n");
		return -EINVAL;
	}

	keypad_data = kzalloc(sizeof(struct omap4_keypad), GFP_KERNEL);
	if (!keypad_data) {
		dev_err(&pdev->dev, "keypad_data memory allocation failed\n");
		return -ENOMEM;
	}

	keypad_data->irq = irq;
    keypad_data->res_start = res_mem[0];
    keypad_data->res_size  = res_mem[1];

	error = omap4_keypad_parse_dt(&pdev->dev, keypad_data);
	if (error)
		goto err_free_keypad;

	res = request_mem_region(keypad_data->res_start, keypad_data->res_size, pdev->name);
	if (!res) {
		dev_err(&pdev->dev, "can't request mem region\n");
		error = -EBUSY;
		goto err_free_keypad;
	}

	keypad_data->base = ioremap(keypad_data->res_start, keypad_data->res_size);
	if (!keypad_data->base) {
		dev_err(&pdev->dev, "can't ioremap mem resource\n");
		error = -ENOMEM;
		goto err_release_mem;
	}


	/*
	 * Enable clocks for the keypad module so that we can read
	 * revision register.
	 */
	pm_runtime_enable(&pdev->dev);
	error = pm_runtime_get_sync(&pdev->dev);
	if (error) {
		dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
		goto err_unmap;
	}
	rev = __raw_readl(keypad_data->base + OMAP4_KBD_REVISION);
	rev &= 0x03 << 30;
	rev >>= 30;
	switch (rev) {
	case KBD_REVISION_OMAP4:
		keypad_data->reg_offset = 0x00;
		keypad_data->irqreg_offset = 0x00;
        keypad_data->irq_clear_reg_off = OMAP4_KBD_IRQENABLE;
        keypad_data->irq_clear_reg_val = OMAP4_VAL_IRQDISABLE;
		break;
	case KBD_REVISION_OMAP5:
		keypad_data->reg_offset = 0x10;
		keypad_data->irqreg_offset = 0x0c;
        keypad_data->irq_clear_reg_off = OMAP4_KBD_IRQENABLE;
        keypad_data->irq_clear_reg_val = OMAP4_VAL_IRQDISABLE;
//        keypad_data->irq_clear_reg_off = OMAP5_KBD_IRQCLEAR;
//        keypad_data->irq_clear_reg_val = OMAP5_VAL_IRQDISABLE;

        dev_warn(&pdev->dev, "KBD_REVISION_OMAP5\n");
		break;
	default:
		dev_err(&pdev->dev,
			"Keypad reports unsupported revision %d", rev);
		error = -EINVAL;
		goto err_pm_put_sync;
	}

	/* input device allocation */
	keypad_data->input = input_dev = input_allocate_device();
	if (!input_dev) {
		error = -ENOMEM;
		goto err_pm_put_sync;
	}

	input_dev->name = pdev->name;
	input_dev->dev.parent = &pdev->dev;
	input_dev->id.bustype = BUS_HOST;
	input_dev->id.vendor = 0x0001;
	input_dev->id.product = 0x0001;
	input_dev->id.version = 0x0001;

	input_dev->open = omap4_keypad_open;
	input_dev->close = omap4_keypad_close;

	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
	if (!keypad_data->no_autorepeat)
		__set_bit(EV_REP, input_dev->evbit);

	input_set_drvdata(input_dev, keypad_data);

	keypad_data->row_shift = get_count_order(keypad_data->cols);
	max_keys = keypad_data->rows << keypad_data->row_shift;
	keypad_data->keymap = kzalloc(max_keys * sizeof(keypad_data->keymap[0]),
				      GFP_KERNEL);
	if (!keypad_data->keymap) {
		dev_err(&pdev->dev, "Not enough memory for keymap\n");
		error = -ENOMEM;
		goto err_free_input;
	}

	error = matrix_keypad_build_keymap(NULL, NULL,
					   keypad_data->rows, keypad_data->cols,
					   keypad_data->keymap, input_dev);
	if (error) {
		dev_err(&pdev->dev, "failed to build keymap\n");
		goto err_free_keymap;
	}

	error = request_threaded_irq(keypad_data->irq, omap4_keypad_irq_handler,
				     omap4_keypad_irq_thread_fn, 0,
				     "omap4-keypad", keypad_data);
	if (error) {
		dev_err(&pdev->dev, "failed to register interrupt\n");
		goto err_free_input;
	}

//	device_init_wakeup(&pdev->dev, true);
	pm_runtime_put_sync(&pdev->dev);

	error = input_register_device(keypad_data->input);
	if (error < 0) {
		dev_err(&pdev->dev, "failed to register input device\n");
		goto err_pm_disable;
	}

	platform_set_drvdata(pdev, keypad_data);
    dev_warn(&pdev->dev, "inited\n");
	return 0;

err_pm_disable:
	pm_runtime_disable(&pdev->dev);
	device_init_wakeup(&pdev->dev, false);
	free_irq(keypad_data->irq, keypad_data);
err_free_keymap:
	kfree(keypad_data->keymap);
err_free_input:
	input_free_device(input_dev);
err_pm_put_sync:
	pm_runtime_put_sync(&pdev->dev);
err_unmap:
	iounmap(keypad_data->base);
err_release_mem:
	release_mem_region(keypad_data->res_start, keypad_data->res_size);
err_free_keypad:
	kfree(keypad_data);
	return error;
}

static int omap4_keypad_remove(struct platform_device *pdev)
{
	struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);
//	struct resource *res;

	free_irq(keypad_data->irq, keypad_data);

	pm_runtime_disable(&pdev->dev);

	device_init_wakeup(&pdev->dev, false);

	input_unregister_device(keypad_data->input);

	iounmap(keypad_data->base);

//	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	release_mem_region(keypad_data->res_start, keypad_data->res_size);

	kfree(keypad_data->keymap);
	kfree(keypad_data);

	return 0;
}

static const struct of_device_id omap_keypad_dt_match[] = {
	{ .compatible = "ti,omap4-keypad" },
	{},
};
MODULE_DEVICE_TABLE(of, omap_keypad_dt_match);

#ifdef CONFIG_PM_SLEEP
static int omap4_keypad_suspend(struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);
	int error;

	if (device_may_wakeup(&pdev->dev)) {
		error = enable_irq_wake(keypad_data->irq);
		if (!error)
			keypad_data->irq_wake_enabled = true;
	}

	return 0;
}

static int omap4_keypad_resume(struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);

	if (device_may_wakeup(&pdev->dev) && keypad_data->irq_wake_enabled) {
		disable_irq_wake(keypad_data->irq);
		keypad_data->irq_wake_enabled = false;
	}

	return 0;
}
#endif

static SIMPLE_DEV_PM_OPS(omap4_keypad_pm_ops,
			 omap4_keypad_suspend, omap4_keypad_resume);

static struct platform_driver omap4_keypad_driver = {
	.probe		= omap4_keypad_probe,
	.remove		= omap4_keypad_remove,
	.driver		= {
		.name	= "omap4-keypad",
		.pm	= &omap4_keypad_pm_ops,
		.of_match_table = omap_keypad_dt_match,
	},
};
module_platform_driver(omap4_keypad_driver);

MODULE_AUTHOR("Texas Instruments");
MODULE_DESCRIPTION("OMAP4 Keypad Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:omap4-keypad");

  • The software team have been notified. They will respond here.
  • Hello jinhu,

    Can you do some more experiments?
    Enable the kernel function tracers in order to be able to enable/disable the interrupts or preemption or both to find a clue on this issue.
    Kernel hacking ---> [*] Tracers ---> [*] Interrupts-off Latency Tracer
    [*] Preemption-off Latency Tracer
    Please, see the <Processor SDK>/board-support/linux-<version>/Documentation/trace/ftrace.txt for more details.
    Also, does the omap4-keypad.c driver behaves in same manner without your modifications?


    Best regards,
    Kemal

  • omap4-keypad.c driver I only add dt support.

    The row0 and row1 are not used for keypad, so I define a row offset.

    all of my modification are not about with irq.

    I try Tracers 

    when I  "echo irqsoff > /debugfs/tracing/current_tracer", I can not see anything about omap4_keypad_irq_handler (irq funcion of driver)

    also preemptirqsoff and preemptoff.

    when I  "echo function_graph > /debugfs/tracing/current_tracer" and "echo omap4_keypad_irq_handler > set_graph_functions",  I can see only omap4_keypad_irq_handler, but not calling stack, even if I try to enable all options.

    I think there is some register error or hardware issue, because if I try to clear keypad interrupt by KBD_IRQENABLE_CLR, the irq will come all the time automatically but I do not press any key.

    I trace the code by prink, the function __handle_domain_irq in kerenl/irq/irqdesc.c also delay sometimes for keypad.

    but I can not printk in caller of  __handle_domain_irq,  which is in arch/arm/kernel/irq.c

    can you test behavior of KBD_IRQENABLE_CLR in the hardware of yours, is it right? 

    can you give me some other suggestions?

  • Check this thread, also try with different keypad module if possible.

  • The thread says the problem is caused by clock source.

    But the keypad controller of am572x has no other clock source.

    So the problem still no resolved.