Created
October 7, 2019 18:02
-
-
Save tablatronix/318368fd0f66958f413f0ac24a2a50e9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* aw9523.c aw9523 martix key | |
* | |
* Version: v1.0.1 | |
* | |
* Copyright (c) 2017 AWINIC Technology CO., LTD | |
* | |
* Author: Nick Li <[email protected]> | |
* | |
* 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("[email protected]"); | |
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