aw9523芯片驱动

硬件连接图

AW9523 是一款 IIC 串口转 16 路 GPIO 并口控制器,8 路 PUSH-PULL 驱动,8 路OPEN-DRAIN 或 PUSH-PULL 驱动可选。芯片上电后,GPIO 口为输出状态,中断使能有效。输出电平状态取决于两位 IIC 地址引脚。AW9523 支持引脚关断功能(PDN),低有效。重新使能后,内部电路处于默认状态。对每一个输入信号作 8us 去抖动处理,当中断使能时,输入状态变化产生中断请求(INTN)。中断为开漏输出,低有效。通过接口读输入状态寄存器清除中断

芯片介绍

寄存器列表

ADDR W/R Default Value(HEX) Function Description
00H R xxH INPUT_PORT0 P0x口输入状态
01H R xxH INPUT_PORT1 P1x 口输入状态
02H W/R xxH OUTPUT_PORT0 P0x口输出驱动逻辑,默认值与i2c地址引脚相关
03H W/R xxH OUTPUT_PORT0 P1x口输出驱动逻辑,默认值与i2c地址引脚相关
04H W/R 00H CFG_PORT0 P0x 口输入或输出选择
05H W/R 00H CFG_PORT1 P1x 口输入或输出选择
06H W/R 00H INTN_PORT0 P0x 口中断使能
07H W/R 00H INTN_PORT1 P1x 口中断使能
08H-10H
11H W/R 00H GPOMD P0x口选择OPEN-DRAIN驱动或 PUSH-PULL驱动
12H-7EH
7FH W 00H RESET 写00H复位

GPIO输入输出方向选择

CFG_PORT0 和 CFG_PORT1 设定端口为输入、输出状态。寄存器每一比特对应某个 GPIO端口,该位置’1’代表输入状态,置’0’代表输出状态。AW9523 上电后,默认为输出状态。

GPIO输出驱动

P0x 端口默认为 OPEN-DRAIN 驱动,可通过配置 GPOMD 寄存器设定为 PUSH-PULL 驱动。P1x 端口为 PUSH-PULL 驱动。开漏输出时,通过外部电阻上拉驱动高电平。

GPIO 口默认驱动逻辑取决于 AD0/AD1 两个引脚

AD1 AD0 P17 P16 P15 P14 P13 P12 P11 P10 P07 P06 P05 P04 P03 P02 P01 P00
GND GND 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
GND VCC 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1
VCC GND 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0
VCC VCC 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

GPIO输入状态查询

通过 IIC 接口读 INPUT_PORT0 和 INPUT_PORT1 可获得当前 GPIO 端口逻辑状态。AW9523 GPIO 口支持 1.8V 高电平输入。

中断功能

当 GPIO 口配置成输入状态,且使能中断功能后,该 IO 口的输入状态变化将产生中断请求。默认情况下,16 路 GPIO 口中断使能。AW9523 时刻监测输入状态,当检测到 IO 口逻辑电平变化,内部电路进行 8us 去抖动处理。若 8us 后,确认该状态变化,则中断引脚上电平拉低。

通过读 INPUT_PORT0 和 INPUT_PORT1 寄存器清除中断。由 P0x 变化产生的中断,必须读INPUT_PORT0 寄存器清除;由 P1x 变化产生的中断,必须读 INPUT_PORT1 寄存器清除。不可
跨组清除中断。

复位功能

AW9523 支持引脚复位和软复位。拉低 PDN 引脚,AW9523 内部电路处于复位状态直至 PDN引脚拉高。或通过 IIC 接口对 RESET 寄存器写入数据 00H,内部电路也将完成一次复位。

寄存器详细描述

INPUT_PORT0(00H) ,输入状态寄存器

名称 描述 默认值
D7 INPUT_P07 P07 引脚当前逻辑状态,0-低电平,1-高电平 X
D6 INPUT_P06 P06 引脚当前逻辑状态,0-低电平,1-高电平 X
D5 INPUT_P05 P05 引脚当前逻辑状态,0-低电平,1-高电平 X
D4 INPUT_P04 P04 引脚当前逻辑状态,0-低电平,1-高电平 X
D3 INPUT_P03 P03 引脚当前逻辑状态,0-低电平,1-高电平 X
D2 INPUT_P02 P02 引脚当前逻辑状态,0-低电平,1-高电平 X
D1 INPUT_P01 P01 引脚当前逻辑状态,0-低电平,1-高电平 X
D0 INPUT_P00 P00 引脚当前逻辑状态,0-低电平,1-高电平 X

INPUT_PORT1(01H),输入状态寄存器

名称 描述 默认值
D7 INPUT_P17 P17 引脚当前逻辑状态,0-低电平,1-高电平 X
D6 INPUT_P16 P16 引脚当前逻辑状态,0-低电平,1-高电平 X
D5 INPUT_P15 P15 引脚当前逻辑状态,0-低电平,1-高电平 X
D4 INPUT_P14 P14 引脚当前逻辑状态,0-低电平,1-高电平 X
D3 INPUT_P13 P13 引脚当前逻辑状态,0-低电平,1-高电平 X
D2 INPUT_P12 P12 引脚当前逻辑状态,0-低电平,1-高电平 X
D1 INPUT_P11 P11 引脚当前逻辑状态,0-低电平,1-高电平 X
D0 INPUT_P10 P10 引脚当前逻辑状态,0-低电平,1-高电平 X

OUTPUT_PORT0(02H),输出状态寄存器

AD1/AD0外接GND/GND时,输出默认值

名称 描述 默认值
D7 OUTPUT_P07 P07 引脚输出状态,0-低电平,1-高电平 0
D6 OUTPUT_P06 P06 引脚输出状态,0-低电平,1-高电平 0
D5 OUTPUT_P05 P05 引脚输出状态,0-低电平,1-高电平 0
D4 OUTPUT_P04 P04 引脚输出状态,0-低电平,1-高电平 0
D3 OUTPUT_P03 P03 引脚输出状态,0-低电平,1-高电平 0
D2 OUTPUT_P02 P02 引脚输出状态,0-低电平,1-高电平 0
D1 OUTPUT_P01 P01 引脚输出状态,0-低电平,1-高电平 0
D0 OUTPUT_P00 P00 引脚输出状态,0-低电平,1-高电平 0

OUTPUT_PORT1(03H),输出状态寄存器

AD1/AD0外接GND/GND时,输出默认值

名称 描述 默认值
D7 OUTPUT_P17 P17 引脚输出状态,0-低电平,1-高电平 0
D6 OUTPUT_P16 P16 引脚输出状态,0-低电平,1-高电平 0
D5 OUTPUT_P15 P15 引脚输出状态,0-低电平,1-高电平 0
D4 OUTPUT_P14 P14 引脚输出状态,0-低电平,1-高电平 0
D3 OUTPUT_P13 P13 引脚输出状态,0-低电平,1-高电平 0
D2 OUTPUT_P12 P12 引脚输出状态,0-低电平,1-高电平 0
D1 INPUT_P11 P11 引脚输出状态,0-低电平,1-高电平 0
D0 OUTPUT_P10 P10 引脚输出状态,0-低电平,1-高电平 0

INTN_PORT0(06H),中断使能寄存器

名称 描述 默认值
D7 INTN_P07 P07 引脚中断使能控制,0-使能,1-关断 0
D6 INTN_P06 P06 引脚中断使能控制,0-使能,1-关断 0
D5 INTN_P05 P05 引脚中断使能控制,0-使能,1-关断 0
D4 INTN_P04 P04 引脚中断使能控制,0-使能,1-关断 0
D3 INTN_P03 P03 引脚中断使能控制,0-使能,1-关断 0
D2 INTN_P02 P02 引脚中断使能控制,0-使能,1-关断 0
D1 INTN_P01 P01 引脚中断使能控制,0-使能,1-关断 0
D0 INTN_P00 P00 引脚中断使能控制,0-使能,1-关断 0

INTN_PORT1(07H),中断使能寄存器

名称 描述 默认值
D7 INTN_P17 P17 引脚中断使能控制,0-使能,1-关断 0
D6 INTN_P16 P16 引脚中断使能控制,0-使能,1-关断 0
D5 INTN_P15 P15 引脚中断使能控制,0-使能,1-关断 0
D4 INTN_P14 P14 引脚中断使能控制,0-使能,1-关断 0
D3 INTN_P13 P13 引脚中断使能控制,0-使能,1-关断 0
D2 INTN_P12 P12 引脚中断使能控制,0-使能,1-关断 0
D1 INTN_P11 P11 引脚中断使能控制,0-使能,1-关断 0
D0 INTN_P10 P10 引脚中断使能控制,0-使能,1-关断 0

GPIO配置表

AW9523(0x58)主要是用来连接各种灯的

AW9523(0X58) 引脚连接
P00 3G_SIGNAL_LED_B
P01 3G_SIGNAL_LED_R
P02 3G_SIGNAL_LED_G
P03 LED2G4
P04 5GLED
P05 LAN LED
P06 SIGNAL_LED_1
P07 SIGNAL_LED_2
P14 SIGNAL_LED_3
P15 SIGNAL_LED_4
SCL GPIO11(mdm9x40 i2c_3)
SDA GPIO10(mdm9x40 i2c_3)
PDN GPIO99(mdm9x40)

AW9523(0x5A)主要是用来连接按键的

AW9523(0X5A) 引脚连接
P00 WPS_KEY
P01 RESET_KEY
SCL GPIO11(mdm9x40 i2c_3)
SDA GPIO10(mdm9x40 i2c_3)
INTN GPIO94(mdm9x40)
PDN GPIO99(mdm9x40)

设备树配置

相关的文件:

  1. kernel/arch/arm/boot/dts/qcom/mdm9640.dtsi
  2. kernel/arch/arm/boot/dts/qcom/mdm9640-pinctrl.dtsi
         aw9523_pins_rst0: aw9523_pins_rst0 {                                                   
              mux {
                      pins = "gpio99";
                      function = "gpio";
                  };
              config {
                      pins = "gpio99";
                      drive-strength = <12>;
                      bias-disable = <0>;
                      output-low;
                    };
            };
            aw9523_pins_rst1: aw9523_pins_rst1 {
                    mux {
                            pins = "gpio99";
                            function = "gpio";
                    };
                    config {
                            pins = "gpio99";
                            drive-strength = <12>;
                            bias-disable = <0>;
                            output-high;
                    };
            };
            aw9523_pins_eint_as_int: aw9523_pins_eint_as_int {
                    mux {
                            pins = "gpio94";
                            function = "gpio";
                    };
                    config {
                            pins = "gpio94";
                            drive-strength = <10>;
                            input-enable;
                            bias-pull-up;

                    };
            };

&i2c_3 {
        aw9523_led: aw9523_led@58 {
            compatible = "awinic,aw9523_led";
            reg = <0x58>;
            reset-gpio = <&tlmm_pinmux 99 0>;
            status = "okay";
            aw9523,led {
                aw9523,name = "aw9523_led";
                aw9523,imax = <3>; 
                aw9523,brightness = <32>;
                aw9523,max_brightness = <255>;
            };   
        };   

        aw9523_key: aw9523_key@5A {
            compatible = "awinic,aw9523_key";                                                   
            reg = <0x5a>;
            awinic,reset-gpio = <&tlmm_pinmux 99 0>;
            awinic,irq-gpio = <&tlmm_pinmux 94 0>;
            pinctrl-names = "aw9523_reset_low", "aw9523_reset_high", "aw9523_eint";
            pinctrl-0 = <&aw9523_pins_rst0>;
            pinctrl-1 = <&aw9523_pins_rst1>;
            pinctrl-2 = <&aw9523_pins_eint_as_int>;
            status = "okay";
        };   
};

AW9523驱动流程

相关文件:

  1. /kernel/driver/i2c/aw9523/aw9523_key.c
  2. /kernel/driver/i2c/aw9523/aw9523_led.c
    /* 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__);
    }                                                                          

pinctrl子系统,选择rst引脚状态

    /* 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);

注册input子系统

源代码

/*
 * aw9523.c   aw9523 martix key
 *
 * Version: v1.0.1
 *
 * Copyright (c) 2017 AWINIC Technology CO., LTD
 *
 *  Author: Nick Li 
 *
 * 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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include   
#include 
#include 
#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<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<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");

剑气纵横三万里

“为什么要努力?” “想去的地方很远,想要的东西很贵,喜欢的人很优秀,父母的白发,朋友的约定,周围人的嘲笑,以及,天生傲骨。”

留下你的评论

*评论支持代码高亮<pre class="prettyprint linenums">代码</pre>

相关推荐