oleavr-rgl-a500-mini-linux-.../drivers/input/keyboard/sunxi-keypad.c
Ole André Vadla Ravnås 169c65d57e Initial commit
2022-05-07 01:01:45 +02:00

435 lines
11 KiB
C
Executable file
Raw Blame History

/*
* Copyright (c) 2013-2015 qinyongshen@allwinnertech.com,qys<qinyongshen@allwinnertech.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/keyboard.h>
#include <linux/ioport.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/timer.h>
#include <linux/clk.h>
#include <linux/irq.h>
#include <linux/of_platform.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/pm.h>
#include <linux/arisc/arisc.h>
#include "sunxi-keypad.h"
#include <linux/power/scenelock.h>
enum {
DEBUG_INIT = 1U << 0,
DEBUG_INT = 1U << 1,
DEBUG_DATA_INFO = 1U << 2,
DEBUG_SUSPEND = 1U << 3,
DEBUG_DATA = 1U << 4,
};
static u32 debug_mask = 0;
#define dprintk(level_mask, fmt, arg...) if (unlikely(debug_mask & level_mask)) \
pr_debug(fmt , ## arg)
module_param_named(debug_mask, debug_mask, int, 0644);
static char keypad_dev_name[] = "kp0";
static struct input_dev *keypd_dev;
static struct sunxi_keypad_data *kpad_data;
static u32 key_pressed[2];
static int keypad_power_key;
#ifdef CONFIG_PM
static struct dev_pm_domain keypad_pm_domain;
#endif
static void keypad_enable_set(u32 enable)
{
u32 reg_val;
reg_val = readl((const volatile void __iomem *)KP_CTL);
if(enable)
reg_val |= 0x1;
else
reg_val &= ~0x1;
writel(reg_val, (volatile void __iomem *)KP_CTL);
}
static void keypad_bitmasks_set(u32 b_masks)
{
u32 reg_val;
b_masks &= 0xffff;
reg_val = readl((const volatile void __iomem *)KP_CTL);
reg_val &= ~(0xffff<<8);
reg_val |= b_masks<<8;
writel(reg_val, (volatile void __iomem *)KP_CTL);
}
static void keypad_ints_set(u32 ints)
{
u32 reg_val;
ints &= 0x3;
reg_val = readl((const volatile void __iomem *)KP_INT_CFG);
reg_val &= ~(0x3<<0);
reg_val |= ints<<0;
writel(reg_val, (volatile void __iomem *)KP_INT_CFG);
}
static int keypad_clk_cfg(void)
{
unsigned long rate = 0; /* 3Mhz */
rate = clk_get_rate(kpad_data->pclk);
dprintk(DEBUG_INIT, "%s: get keypad_clk_source rate %dHZ\n", __func__, (__u32)rate);
if(clk_set_parent(kpad_data->mclk, kpad_data->pclk))
pr_err("%s: set keypad_clk parent to keypad_clk_source failed!\n", __func__);
if (clk_set_rate(kpad_data->mclk, 31250)) {
pr_err("set keypad clock freq to 31250 failed!\n");
}
rate = clk_get_rate(kpad_data->mclk);
dprintk(DEBUG_INIT, "%s: get keyapd_clk rate %dHZ\n", __func__, (__u32)rate);
if (clk_prepare_enable(kpad_data->mclk)) {
pr_err("try to enable keypad_clk failed!\n");
}
return 0;
}
static void keypad_clk_uncfg(void)
{
if(NULL == kpad_data->mclk || IS_ERR(kpad_data->mclk)) {
pr_err("keypad_clk handle is invalid, just return!\n");
return;
} else {
clk_disable_unprepare(kpad_data->mclk);
clk_put(kpad_data->mclk);
kpad_data->mclk = NULL;
}
if(NULL == kpad_data->pclk || IS_ERR(kpad_data->pclk)) {
pr_err("keypad_clk_source handle is invalid, just return!\n");
return;
} else {
clk_put(kpad_data->pclk);
kpad_data->pclk = NULL;
}
return;
}
static void keypad_set_scan_cycl(u32 cycl)
{
u32 reg_val;
cycl = (cycl > 0xffff? 0xffff:cycl);
reg_val = readl((const volatile void __iomem *)KP_TIMING);
reg_val &= ~(0xffff<<0);
reg_val |= cycl<<0;
writel(reg_val, (volatile void __iomem *)KP_TIMING);
}
static void keypad_set_debun_cycl(u32 cycl)
{
u32 reg_val;
cycl = (cycl > 0xffff? 0xffff:cycl);
reg_val = readl((const volatile void __iomem *)KP_TIMING);
reg_val &= ~(0xffff<<16);
reg_val |= cycl<<16;
writel(reg_val, (volatile void __iomem *)KP_TIMING);
}
static int sunxi_keypad_startup(struct platform_device *pdev)
{
int ret = 0;
struct device_node *np =NULL;
np = pdev->dev.of_node;
if (!of_device_is_available(np)) {
pr_err("%s: sunxi keyboard is disable\n", __func__);
return -EPERM;
}
key_pressed[0] = 0;
key_pressed[1] = 0;
kpad_data = kzalloc(sizeof(*kpad_data), GFP_KERNEL);
if (IS_ERR_OR_NULL(kpad_data)) {
pr_err("kpad_data: not enough memory for keypad data\n");
return -ENOMEM;
}
kpad_data->reg_base= of_iomap(np, 0);
if (NULL == kpad_data->reg_base) {
pr_err("%s:Failed to ioremap() io memory region.\n",__func__);
ret = -EBUSY;
}else
dprintk(DEBUG_INIT, "key base: %p !\n",kpad_data->reg_base);
kpad_data->irq_num= irq_of_parse_and_map(np, 0);
if (0 == kpad_data->irq_num) {
pr_err("%s:Failed to map irq.\n", __func__);
ret = -EBUSY;
}else
dprintk(DEBUG_INIT, "keypad irq num: %d !\n",kpad_data->irq_num);
kpad_data->pclk = of_clk_get(np, 0);
kpad_data->mclk = of_clk_get(np, 1);
if (NULL==kpad_data->pclk||IS_ERR(kpad_data->pclk)
||NULL==kpad_data->mclk||IS_ERR(kpad_data->mclk)) {
dprintk(DEBUG_INIT, "%s:keypad has no clk.\n", __func__);
}
if (of_property_read_u32(np, "keypad_power_key_code", &keypad_power_key)) {
pr_err("%s: get keypad_power_key_code failed", __func__);
ret = -EBUSY;
}
return ret;
}
static u32 keypad_read_int(void)
{
return readl((const volatile void __iomem *)KP_INT_STA);
}
static void keypad_clr_int(u32 reg_val)
{
writel(reg_val, (volatile void __iomem *)KP_INT_STA);
}
static int sunxi_keypad_suspend(struct device *dev)
{
dprintk(DEBUG_SUSPEND, "enter: sunxi_keypad_suspend. \n");
disable_irq_nosync(kpad_data->irq_num);
if(IS_ERR_OR_NULL(kpad_data->mclk)) {
pr_err("keypad_clk handle is invalid, just return!\n");
return -1;
} else {
clk_disable_unprepare(kpad_data->mclk);
}
return 0;
}
/* <20><><EFBFBD>»<EFBFBD><C2BB><EFBFBD> */
static int sunxi_keypad_resume(struct device *dev)
{
unsigned int keypad_event = 0;
dprintk(DEBUG_SUSPEND, "enter: sunxi_keypad_resume. \n");
#if 0
arisc_query_wakeup_source(&keypad_event);
#endif
dprintk(DEBUG_SUSPEND, "%s: event 0x%x\n", __func__, keypad_event);
if (CPUS_WAKEUP_IR&keypad_event) {
input_report_key(keypd_dev, keypad_power_key, 1);
input_sync(keypd_dev);
msleep(1);
input_report_key(keypd_dev, keypad_power_key, 0);
input_sync(keypd_dev);
}
key_pressed[0] = 0;
key_pressed[1] = 0;
clk_prepare_enable(kpad_data->mclk);
enable_irq(kpad_data->irq_num);
return 0;
}
static irqreturn_t keypad_irq_service(int irq, void *dummy)
{
u32 int_stat,key_scan[2];
u32 tmp_reg,i,j;
//dprintk(DEBUG_INT, "keypad int enter\n");
int_stat = keypad_read_int();
key_scan[0] = ~(readl((const volatile void __iomem *)KP_IN0));
key_scan[1] = ~(readl((const volatile void __iomem *)KP_IN1));
dprintk(DEBUG_INT, "keyscan 0 : 0x%x \n",key_scan[0]);
dprintk(DEBUG_INT, "keyscan 1 : 0x%x \n",key_scan[1]);
if(int_stat & INT_KEY_PRESS){
dprintk(DEBUG_INT, "keypad:key pressed \n");
for(j = 0; j < 2; j++){
tmp_reg = key_scan[j] & (key_pressed[j]^key_scan[j]);
if(tmp_reg){
for(i = 0; tmp_reg; i++){
if(tmp_reg & 0x1){
input_report_key(keypd_dev, keypad_keycodes[i+j*32], 1);
input_sync(keypd_dev);
dprintk(DEBUG_DATA, "irq key down :%d\n",keypad_keycodes[i+j*32]);
}
tmp_reg = tmp_reg>>1;
}
}
tmp_reg = key_pressed[j] & (key_pressed[j]^key_scan[j]);
if(tmp_reg){
for(i = 0; tmp_reg; i++){
if(tmp_reg & 0x1){
input_report_key(keypd_dev, keypad_keycodes[i+j*32], 0);
input_sync(keypd_dev);
dprintk(DEBUG_DATA, "irq key up :%d\n",keypad_keycodes[i+j*32]);
}
tmp_reg = tmp_reg>>1;
}
}
key_pressed[j] = key_scan[j];
}
}
if(int_stat & INT_KEY_RELEASE){
dprintk(DEBUG_INT, "keypad:key release \n");
for(j = 0; j < 2; j++){
tmp_reg = key_pressed[j];
if(tmp_reg){
for(i = 0; tmp_reg; i++){
if(tmp_reg & 0x1){
input_report_key(keypd_dev, keypad_keycodes[i+j*32], 0);
input_sync(keypd_dev);
dprintk(DEBUG_DATA, "irq key up :%d\n",keypad_keycodes[i+j*32]);
}
tmp_reg = tmp_reg>>1;
}
}
key_pressed[j] = 0;
}
}
//key_pressed[0] = key_scan[0];
//key_pressed[1] = key_scan[1];
keypad_clr_int(int_stat);
return IRQ_HANDLED;
}
#ifdef CONFIG_OF
/*
* Translate OpenFirmware node properties into platform_data
*/
static struct of_device_id sunxi_keypad_of_match[] = {
{ .compatible = "allwinner,keypad",},
{ },
};
MODULE_DEVICE_TABLE(of, sunxi_keypad_of_match);
#else /* !CONFIG_OF */
#endif
static int sunxi_keypad_probe(struct platform_device *pdev)
{
int i;
int err =0;
dprintk(DEBUG_INIT, "sunxi key pad_init \n");
if (pdev->dev.of_node) {
/* get dt and sysconfig */
err = sunxi_keypad_startup(pdev);
}else{
pr_err("sunxi keypad device tree err!\n");
return -EBUSY;
}
keypd_dev = input_allocate_device();
if (!keypd_dev) {
pr_err("keypd_dev: not enough memory for input device\n");
err = -ENOMEM;
goto fail1;
}
keypd_dev->name = "sunxi-keypad";
keypd_dev->phys = "Keypad/input2";
keypd_dev->id.bustype = BUS_HOST;
keypd_dev->id.vendor = 0x0001;
keypd_dev->id.product = 0x0001;
keypd_dev->id.version = 0x0100;
keypd_dev->dev.init_name = &keypad_dev_name[0];
#ifdef REPORT_REPEAT_KEY_VALUE
keypd_dev->evbit[0] = BIT_MASK(EV_KEY)|BIT_MASK(EV_REP) ;
#else
keypd_dev->evbit[0] = BIT_MASK(EV_KEY);
#endif
for (i = 0; i < KEYPAD_MAX_CNT; i++)
set_bit(keypad_keycodes[i], keypd_dev->keybit);
err = input_register_device(keypd_dev);
if (err){
pr_err("keypd_dev: input dev register fail\n");
goto fail2;
}
kpad_data->input_dev= keypd_dev;
platform_set_drvdata(pdev, kpad_data);
keypad_clk_cfg();
keypad_set_scan_cycl(KEYPAD_SCAN_CYCL);
keypad_set_debun_cycl(KEYPAD_DEBOUN_CYCL);
keypad_bitmasks_set(PASS_ALL);
keypad_ints_set(INT_KEYPRESS_EN|INT_KEYRELEASE_EN);
keypad_enable_set(KEYPAD_ENABLE);
if (request_irq(kpad_data->irq_num, keypad_irq_service, 0, "Sunxi_keypad",
keypd_dev)) {
err = -EBUSY;
pr_err("keypd_dev: irq request failed\n");
goto fail3;
}
dprintk(DEBUG_INIT, "sunxi_keypad_init end\n");
return 0;
fail3:
input_unregister_device(keypd_dev);
fail2:
input_free_device(keypd_dev);
fail1:
if(kpad_data)
kfree(kpad_data);
pr_err("sunxikbd_init failed. \n");
return err;
}
static int sunxi_keypad_remove(struct platform_device *pdev)
{
free_irq(kpad_data->irq_num, NULL);
keypad_clk_uncfg();
input_unregister_device(keypd_dev);
input_free_device(keypd_dev);
return 0;
}
static const struct dev_pm_ops sunxi_keypad_pm_ops = {
.suspend = sunxi_keypad_suspend,
.resume = sunxi_keypad_resume,
};
static struct platform_driver sunxi_keypad_driver = {
.probe = sunxi_keypad_probe,
.remove = sunxi_keypad_remove,
.driver = {
.name = "sunxi-keypad",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(sunxi_keypad_of_match),
.pm = &sunxi_keypad_pm_ops,
},
};
module_platform_driver(sunxi_keypad_driver);
MODULE_AUTHOR(" Qin Yongshen");
MODULE_DESCRIPTION("sunxi-keypad driver");
MODULE_LICENSE("GPL");