Skip to content

Instantly share code, notes, and snippets.

@tablatronix
Created October 7, 2019 18:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tablatronix/318368fd0f66958f413f0ac24a2a50e9 to your computer and use it in GitHub Desktop.
Save tablatronix/318368fd0f66958f413f0ac24a2a50e9 to your computer and use it in GitHub Desktop.
/*
* aw9523.c aw9523 martix key
*
* Version: v1.0.1
*
* Copyright (c) 2017 AWINIC Technology CO., LTD
*
* Author: Nick Li <liweilei@awinic.com.cn>
*
* 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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/workqueue.h>
#include <linux/errno.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/dma-mapping.h>
#include <linux/hrtimer.h>
#include <linux/input/aw9523_key.h>
#include <linux/uaccess.h>
#define HRTIMER_FRAME 20
#define DISABLE_PO_IRQ 0xff
#define DISABLE_P1_IRQ 0xff
#define ENABLE_PO_IRQ 0xfc
#define ENABLE_P1_IRQ 0xff
#define P0_IRQ_INPUT_MODE 0x03
#define P1_IRQ_INPUT_MODE 0x00
/*register list */
#define P0_INPUT 0x00
#define P1_INPUT 0x01
#define P0_OUTPUT 0x02
#define P1_OUTPUT 0x03
#define P0_CONFIG 0x04
#define P1_CONFIG 0x05
#define P0_INT 0x06
#define P1_INT 0x07
#define ID_REG 0x10
#define CTL_REG 0x11
#define P0_LED_MODE 0x12
#define P1_LED_MODE 0x13
#define P1_0_DIM0 0x20
#define P1_1_DIM0 0x21
#define P1_2_DIM0 0x22
#define P1_3_DIM0 0x23
#define P0_0_DIM0 0x24
#define P0_1_DIM0 0x25
#define P0_2_DIM0 0x26
#define P0_3_DIM0 0x27
#define P0_4_DIM0 0x28
#define P0_5_DIM0 0x29
#define P0_6_DIM0 0x2A
#define P0_7_DIM0 0x2B
#define P1_4_DIM0 0x2C
#define P1_5_DIM0 0x2D
#define P1_6_DIM0 0x2E
#define P1_7_DIM0 0x2F
#define SW_RSTN 0x7F
#define KROW_P0_0 0
#define KROW_P0_1 1
#define KROW_P0_2 2
#define KROW_P0_3 3
#define KROW_P0_4 4
#define KROW_P0_5 5
#define KROW_P0_6 6
#define KROW_P0_7 7
#define KROW_P1_0 8
#define KROW_P1_1 9
#define KROW_P1_2 10
#define KROW_P1_3 11
#define KROW_P1_4 12
#define KROW_P1_5 13
#define KROW_P1_6 14
#define KROW_P1_7 15
#define SPI_DEV_NAME "si3217x"
#define WPSKEY_CODE 293
#define RESETKEY_CODE 294
struct keymap key_map[16]={
[KROW_P0_0] = {"WPS_KEY", WPSKEY_CODE},
[KROW_P0_1] = {"RESET_KEY", RESETKEY_CODE},
};
static unsigned char keyst_old[2];
static unsigned char keyst_def[2] = {0x00, 0x81};
static struct aw9523_kpad_platform_data *aw9523_data = NULL;
/*********************************************************
*
* aw9523 i2c write/read
*
********************************************************/
static int __aw9523_read_reg(struct i2c_client *client, int reg, unsigned char *val)
{
int ret;
ret = i2c_smbus_read_byte_data(client, reg);
if (ret < 0) {
dev_err(&client->dev, "i2c read fail: can't read from %02x: %d\n", reg, ret);
return ret;
} else {
*val = ret;
}
return 0;
}
static int __aw9523_write_reg(struct i2c_client *client, int reg, int val)
{
int ret;
ret = i2c_smbus_write_byte_data(client, reg, val);
if (ret < 0) {
dev_err(&client->dev, "i2c write fail: can't write %02x to %02x: %d\n",
val, reg, ret);
return ret;
}
return 0;
}
static int aw9523_read_reg(struct i2c_client *client, int reg,
unsigned char *val)
{
int rc;
struct aw9523_kpad_platform_data *pdata = NULL;
pdata = i2c_get_clientdata(client);
if (pdata) {
mutex_lock(&pdata->read_write_lock);
rc = __aw9523_read_reg(client, reg, val);
mutex_unlock(&pdata->read_write_lock);
}
return rc;
}
static int aw9523_write_reg(struct i2c_client *client, int reg,
unsigned char val)
{
int rc;
struct aw9523_kpad_platform_data *pdata;
pdata = i2c_get_clientdata(client);
mutex_lock(&pdata->read_write_lock);
rc = __aw9523_write_reg(client, reg, val);
mutex_unlock(&pdata->read_write_lock);
return rc;
}
/*********************************************************
*
* hrtimer work
*
********************************************************/
static void aw9523_key_work(struct work_struct *work)
{
struct aw9523_kpad_platform_data *pdata;
struct i2c_client *client;
unsigned char val;
int i; bool key_val;
pdata = aw9523_data;
client = pdata->client;
aw9523_read_reg(client, P0_INPUT, &val);
if(val != keyst_old[0]){
for(i = 0; i<8; i++){
if(pdata->keymap[i].name[0]=='\0') continue;
if((val& 1<<i) != (keyst_old[0] & 1<<i)){
key_val = ((val& 1<<i) == (keyst_def[0] & 1<<i));
input_report_key(pdata->input, pdata->keymap[i].key_code, key_val);
}
}
keyst_old[0] = val;
}
aw9523_read_reg(client, P1_INPUT, &val);
if(val != keyst_old[1]){
for(i = 0; i<8; i++){
if(pdata->keymap[i+8].name[0]=='\0') continue;
if((val& 1<<i) != (keyst_old[1] & 1<<i)){
key_val = ((val& 1<<i) == (keyst_def[1] & 1<<i));
input_report_key(pdata->input, pdata->keymap[i+8].key_code, key_val);
}
}
keyst_old[1] = val;
}
aw9523_write_reg(client, P0_INT, ENABLE_PO_IRQ); //enable p0 port irq
aw9523_write_reg(client, P1_INT, ENABLE_P1_IRQ); //enable p1 port irq
input_sync(pdata->input);
enable_irq(client->irq);
return;
}
static enum hrtimer_restart aw9523_key_timer_func(struct hrtimer *timer)
{
schedule_work(&aw9523_data->key_work);
return HRTIMER_NORESTART;
}
/*********************************************************
*
* int work
*
********************************************************/
static void aw9523_int_work(struct work_struct *work)
{
struct aw9523_kpad_platform_data *pdata = container_of(work,
struct aw9523_kpad_platform_data, work.work);
struct i2c_client *client = pdata->client;
aw9523_write_reg(client, P0_INT, DISABLE_PO_IRQ); //disable p0 port irq
aw9523_write_reg(client, P1_INT, DISABLE_P1_IRQ); //disable p1 port irq
hrtimer_start(&pdata->key_timer, ktime_set(0,(1000/HRTIMER_FRAME)*1000000), HRTIMER_MODE_REL);
}
static irqreturn_t aw9523_irq(int irq, void *handle)
{
struct i2c_client *client = handle;
struct aw9523_kpad_platform_data *pdata;
int bm_irq_status = 0;
bm_mdm9640_i2c_gpio_read_irq_status(BM_MDM9640_PCIE_WAKE_SET, &bm_irq_status);
if (!bm_irq_status) {
printk("%s, it is pcie wake irq.\n", __func__);
return IRQ_HANDLED;
}
pdata = i2c_get_clientdata(client);
disable_irq_nosync(client->irq);
schedule_delayed_work(&pdata->work, msecs_to_jiffies(pdata->delay));
return IRQ_HANDLED;
}
/*********************************************************
*
* aw9523 reg
*
********************************************************/
static ssize_t aw9523_get_reg(struct device* cd,struct device_attribute *attr, char* buf)
{
unsigned char val = 0;
unsigned char i = 0;
ssize_t len = 0;
struct i2c_client *client = aw9523_data->client;
for(i=0; i<0x30; i++)
{
aw9523_read_reg(client, i, &val);
len += snprintf(buf+len, PAGE_SIZE-len, "reg%2x = 0x%2x, ", i, val);
}
len += snprintf(buf+len, PAGE_SIZE-len, "\n");
return len;
}
static ssize_t aw9523_set_reg(struct device* cd, struct device_attribute *attr, const char* buf, size_t len)
{
unsigned int databuf[2];
struct i2c_client *client = aw9523_data->client;
if(2 == sscanf(buf,"%x %x",&databuf[0], &databuf[1]))
{
aw9523_write_reg(client,databuf[0], databuf[1]);
}
return len;
}
static DEVICE_ATTR(reg, 0660, aw9523_get_reg, aw9523_set_reg);
static int aw9523_create_sysfs(struct i2c_client *client)
{
int err;
struct device *dev = &(client->dev);
err = device_create_file(dev, &dev_attr_reg);
return err;
}
static int aw9523_read_chipid(struct i2c_client *client)
{
unsigned char val;
int ret = 0;
ret = aw9523_read_reg(client, ID_REG, &val);
if(!ret && val != 0x23)
ret = -EINVAL;
return ret;
}
/*********************************************************
*
* aw9523 init
*
********************************************************/
static void aw9523_key_init(struct i2c_client *client)
{
unsigned char val;
aw9523_write_reg(client, P0_INT, DISABLE_PO_IRQ); //disable p0 port irq 0x06
aw9523_write_reg(client, P1_INT, DISABLE_P1_IRQ); //disable p1 port irq 0x07
aw9523_write_reg(client, P0_CONFIG, P0_IRQ_INPUT_MODE); //set p0 port input mode 0x04
aw9523_write_reg(client, P1_CONFIG, P1_IRQ_INPUT_MODE); //set p1 port input mode 0x05
aw9523_write_reg(client,P0_LED_MODE, 0xff);
aw9523_write_reg(client,P0_LED_MODE, 0xff);
aw9523_write_reg(client,CTL_REG, 0x10);
aw9523_write_reg(client,P0_OUTPUT, 0x00);
aw9523_write_reg(client,P1_OUTPUT, 0x3c);
aw9523_read_reg(client, P0_INPUT, &val);
keyst_old[0] = val;
aw9523_read_reg(client, P1_INPUT, &val);
keyst_old[1]= val;
aw9523_write_reg(client, P0_INT, ENABLE_PO_IRQ); //enable p0 port irq 0x06
aw9523_write_reg(client, P1_INT, ENABLE_P1_IRQ); //enable p1 port irq 0x07
}
void bm_mdm9640_i2c_gpio_set(int reg, unsigned char val)
{
struct i2c_client *client = NULL;
unsigned char oldval = 0, setval = 0;
int raw_output_port = 0, c_reg;
if (!aw9523_data)
return ;
client = aw9523_data->client;
val = (val > 0 ? 1 : 0);
if (reg >= 10) {
c_reg = reg - 10;
raw_output_port = P1_OUTPUT;
}
else {
c_reg = reg;
raw_output_port = P0_OUTPUT;
}
aw9523_read_reg(client, raw_output_port, &oldval);
//printk("%s, read oldval: %02hhx\n", __func__, oldval);
setval = oldval;
setval &= (~(0x1 << c_reg));
setval |= (val << c_reg);
//printk("%s, set val: %02hhx, reg:%d, val:%d\n", __func__, setval, reg, val);
aw9523_write_reg(client, raw_output_port, setval);
return ;
}
EXPORT_SYMBOL(bm_mdm9640_i2c_gpio_set);
void bm_mdm9640_i2c_gpio_irq_set(int reg, unsigned char val)
{
struct i2c_client *client = NULL;
unsigned char oldval = 0, setval = 0;
int raw_config_port = 0, raw_int_port = 0, c_reg;
if (!aw9523_data)
return ;
client = aw9523_data->client;
val = (val > 0 ? 1 : 0);
if (reg >= 10) {
c_reg = reg - 10;
raw_config_port = P1_CONFIG;
raw_int_port = P1_INT;
}
else {
c_reg = reg;
raw_config_port = P0_CONFIG;
raw_int_port = P0_INT;
}
// config input
aw9523_read_reg(client, raw_config_port, &oldval);
printk("%s, read oldval: %02hhx\n", __func__, oldval);
setval = oldval;
setval |= (0x1 << c_reg);
printk("%s, set val: %02hhx, reg:%d, val:%d\n", __func__, setval, reg, val);
aw9523_write_reg(client, raw_config_port, setval);
// config input
aw9523_read_reg(client, raw_int_port, &oldval);
printk("%s, read int oldval: %02hhx\n", __func__, oldval);
setval = oldval;
setval &= ~(0x1 << c_reg);
printk("%s, set val: %02hhx, reg:%d, val:%d\n", __func__, setval, reg, val);
aw9523_write_reg(client, raw_int_port, setval);
return ;
}
EXPORT_SYMBOL(bm_mdm9640_i2c_gpio_irq_set);
void bm_mdm9640_i2c_gpio_read_irq_status(int reg, int *irq_st)
{
struct i2c_client *client = NULL;
unsigned char rval = 0;
int raw_input_port = 0, c_reg;
if (!aw9523_data)
return ;
client = aw9523_data->client;
if (reg >= 10) {
c_reg = reg - 10;
raw_input_port = P1_INPUT;
}
else {
c_reg = reg ;
raw_input_port = P0_INPUT;
}
aw9523_read_reg(client, raw_input_port, &rval);
printk("%s, read oldval: %02hhx\n", __func__, rval);
*irq_st = ((rval >> c_reg) & 0x01);
return ;
}
EXPORT_SYMBOL(bm_mdm9640_i2c_gpio_read_irq_status);
int bm_mdm9640_check_aw9523_ready(void)
{
if (aw9523_data)
return 1;
else
return 0;
}
EXPORT_SYMBOL(bm_mdm9640_check_aw9523_ready);
#define PANEL_GPIO 8
static int get_panel_state(void)
{
void __iomem * gpio_panel_addr = ioremap_nocache(0x1000000 + PANEL_GPIO * 0x1000, 8);
writel(0x01,gpio_panel_addr);
return readl(gpio_panel_addr+0x04)&0x01? 1:0;
}
/*********************************************************
*
* aw9523 driver
*
********************************************************/
static int aw9523_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct aw9523_kpad_platform_data *pdata = client->dev.platform_data;
int ret = 0;
int i =0;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
return -EIO;
}
if(!get_panel_state()){
dev_err(&client->dev, "the panel can not use i2c\n");
return -ENOMEM;
}
pdata = devm_kzalloc(&client->dev,sizeof(struct aw9523_kpad_platform_data), GFP_KERNEL);
if (!pdata)
{
dev_err(&client->dev, "Failed to allocate memory\n");
return -ENOMEM;
}
aw9523_data = pdata;
/* seset & int Pins */
pdata->pinctrl = devm_pinctrl_get(&client->dev);
if (IS_ERR(pdata->pinctrl)) {
pr_err("%s:failed to get pinctrl\n", __func__);
goto err;
}
pdata->rst_state_low = pinctrl_lookup_state(pdata->pinctrl, "aw9523_reset_low");
if (IS_ERR(pdata->rst_state_low)) {
pr_err("%s:can not get reset pinstate\n", __func__);
goto err;
}
pdata->rst_state_high = pinctrl_lookup_state(pdata->pinctrl, "aw9523_reset_high");
if (IS_ERR(pdata->rst_state_high)) {
pr_err("%s:can not get reset pinstate\n", __func__);
goto err;
}
pdata->irq_state = pinctrl_lookup_state(pdata->pinctrl, "aw9523_eint");
if (IS_ERR(pdata->irq_state)) {
pr_err("%s:can not get irq pinstate\n", __func__);
goto err;
}
ret= pinctrl_select_state(pdata->pinctrl, pdata->rst_state_high);
if (ret){
pr_err("%s:set reset pin state failed!\n", __func__);
}
ret = pinctrl_select_state(pdata->pinctrl, pdata->irq_state);
if (ret){
pr_err("%s:set irq pin state failed!\n", __func__);
}
pdata->rst_gpio = of_get_named_gpio(client->dev.of_node, "awinic,reset-gpio", 0);
if ((!gpio_is_valid(pdata->rst_gpio))){
goto err;
}
ret = gpio_request(pdata->rst_gpio, "aw9523-reset-keys");
if (ret == 0) {
gpio_set_value(pdata->rst_gpio, 0);
msleep(1);
gpio_set_value(pdata->rst_gpio, 1);
msleep(1);
}else if(ret !=- 16){
dev_err(&client->dev, "%s: unable to request gpio [%d]\n",
__func__, pdata->rst_gpio);
goto err;
}
/* reset & int Pins end*/
/* hardware reset */
pdata->client = client;
mutex_init(&pdata->read_write_lock);
i2c_set_clientdata(client, pdata);
if(aw9523_read_chipid(client)) {
dev_err(&client->dev, "%s: read_chipid error\n", __func__);
goto err_rst_gpio;
}
INIT_DELAYED_WORK(&pdata->work, aw9523_int_work);
pdata->delay = 10;
/* hardware reset end */
/* key report */
pdata->input = input_allocate_device();
if (!pdata->input) {
dev_err(&client->dev, "%s: failed to allocate input device\n", __func__);
goto err_rst_gpio;
}
pdata->input->name = "aw9523-key";
pdata->input->phys = "aw9523-keys/input0";
pdata->input->dev.parent = &client->dev;
pdata->keymap_len = sizeof(key_map)/sizeof(struct keymap);
pdata->keymap = (struct keymap *)&key_map;
input_set_drvdata(pdata->input, pdata);
__set_bit(EV_KEY, pdata->input->evbit);
__set_bit(EV_SYN, pdata->input->evbit);
for (i = 0; i < pdata->keymap_len; i++){
if(pdata->keymap[i].name[0]=='\0') continue;
__set_bit(pdata->keymap[i].key_code, pdata->input->keybit);
}
ret = input_register_device(pdata->input);
if (ret) {
dev_err(&client->dev, "unable to register input device\n");
goto err_free_input;
}
/* key report end */
/* interrupt work */
pdata->irq_gpio = of_get_named_gpio(client->dev.of_node, "awinic,irq-gpio", 0);
if ((!gpio_is_valid(pdata->irq_gpio))){
goto err_free_dev;
}
ret = gpio_request(pdata->irq_gpio, "aw9523-keys");
if (ret) {
dev_err(&client->dev, "%s: unable to request gpio [%d]\n",
__func__, pdata->irq_gpio);
goto err_free_dev;
}
ret = gpio_direction_input(pdata->irq_gpio);
if (ret) {
dev_err(&client->dev, "%s: unable to set direction for gpio [%d]\n",
__func__, pdata->irq_gpio);
goto err_irq_gpio;
}
client->irq = gpio_to_irq(pdata->irq_gpio);
if (client->irq < 0) {
ret = client->irq;
goto err_irq_gpio;
}
/* hrtimer */
INIT_WORK(&pdata->key_work, aw9523_key_work);
hrtimer_init(&pdata->key_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
pdata->key_timer.function = aw9523_key_timer_func;
/* hrtimer end */
ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
aw9523_irq, IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED,
"aw9523_irq", client);
if (ret) {
dev_err(&client->dev, "%s: failed aw9523 irq=%d request ret = %d\n",
__func__, client->irq, ret);
goto err_irq_gpio;
}else{
disable_irq_nosync(client->irq);
}
device_init_wakeup(&client->dev, 1);
aw9523_create_sysfs(client);
aw9523_key_init(client);
enable_irq(client->irq);
pr_err("%s:%d key success\n", __func__,__LINE__);
return 0;
err_irq_gpio:
cancel_work_sync(&pdata->key_work);
gpio_free(pdata->irq_gpio);
err_free_dev:
input_unregister_device(pdata->input);
err_free_input:
input_free_device(pdata->input);
err_rst_gpio:
gpio_free(pdata->rst_gpio);
mutex_destroy(&pdata->read_write_lock);
err:
kfree(pdata);
aw9523_data = NULL;
pr_err("%s:%d key failed\n", __func__,__LINE__);
return 0;
}
static int aw9523_i2c_remove(struct i2c_client *client)
{
struct aw9523_kpad_platform_data *pdata = i2c_get_clientdata(client);
if(!pdata)
return -1;
aw9523_write_reg(client, 0x00, 0);
free_irq(client->irq, pdata);
cancel_delayed_work_sync(&pdata->work);
cancel_work_sync(&pdata->key_work);
gpio_free(pdata->irq_gpio);
input_unregister_device(pdata->input);
input_free_device(pdata->input);
gpio_free(pdata->rst_gpio);
mutex_destroy(&pdata->read_write_lock);
kfree(pdata);
aw9523_data = NULL;
return 0;
}
static const struct of_device_id aw9523_keypad_of_match[] = {
{ .compatible = "awinic,aw9523_key",},
{},
};
static const struct i2c_device_id aw9523_i2c_id[] = {
{"aw9523_key", 0},
{},
};
MODULE_DEVICE_TABLE(i2c, aw9523_i2c_id);
static struct i2c_driver aw9523_i2c_driver = {
.driver = {
.name = "aw9523_key",
.owner = THIS_MODULE,
.of_match_table = aw9523_keypad_of_match,
},
.probe = aw9523_i2c_probe,
.remove = aw9523_i2c_remove,
.id_table = aw9523_i2c_id,
};
static int __init aw9523_i2c_init(void)
{
int ret = 0;
ret = i2c_add_driver(&aw9523_i2c_driver);
if (ret) {
pr_err("fail to add aw9523 device into i2c\n");
return ret;
}
return 0;
}
subsys_initcall(aw9523_i2c_init);
static void __exit aw9523_i2c_exit(void)
{
i2c_del_driver(&aw9523_i2c_driver);
}
module_exit(aw9523_i2c_exit);
MODULE_AUTHOR("liweilei@awinic.com.cn");
MODULE_DESCRIPTION("AW9523B Keypad driver");
MODULE_LICENSE("GPL");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment