aw9523芯片驱动

硬件连接图

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

aw9523原理图

芯片介绍

寄存器列表

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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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;

};
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
&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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
   /* 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引脚状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
   /* 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子系统

源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
/*
* 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");