1
+ // SPDX-License-Identifier: GPL-2.0-or-later
2
+ /*
3
+ * Analog Devices MAX16150/MAX16169 Pushbutton Driver
4
+ *
5
+ * Copyright 2025 Analog Devices Inc.
6
+ */
7
+
8
+ #include <linux/gpio/consumer.h>
9
+ #include <linux/init.h>
10
+ #include <linux/input.h>
11
+ #include <linux/interrupt.h>
12
+ #include <linux/kernel.h>
13
+ #include <linux/mod_devicetable.h>
14
+ #include <linux/of.h>
15
+ #include <linux/platform_device.h>
16
+
17
+ #define MAX16150_LONG_INTERRUPT 120000000
18
+
19
+ struct max16150_chip_info {
20
+ bool has_clr_gpio ;
21
+ };
22
+
23
+ struct max16150_device {
24
+ const struct max16150_chip_info * chip_info ;
25
+ struct input_dev * input ;
26
+ struct gpio_desc * gpiod ;
27
+ struct gpio_desc * clr_gpiod ;
28
+ u64 low , high , duration ;
29
+ int irq ;
30
+ unsigned int keycode ;
31
+ };
32
+
33
+ static irqreturn_t max16150_irq_handler (int irq , void * _max16150 )
34
+ {
35
+ struct max16150_device * max16150 = _max16150 ;
36
+ int value ;
37
+
38
+ value = gpiod_get_value (max16150 -> gpiod );
39
+
40
+ if (value ) {
41
+ max16150 -> high = ktime_get_ns ();
42
+ if (max16150 -> low ) {
43
+ max16150 -> duration = max16150 -> high - max16150 -> low ;
44
+
45
+ if (max16150 -> duration > MAX16150_LONG_INTERRUPT ) {
46
+ if (max16150 -> chip_info -> has_clr_gpio && max16150 -> clr_gpiod )
47
+ gpiod_set_value (max16150 -> clr_gpiod , 1 );
48
+
49
+ input_report_key (max16150 -> input , max16150 -> keycode , 1 );
50
+ input_sync (max16150 -> input );
51
+ }
52
+ }
53
+ } else {
54
+ max16150 -> low = ktime_get_ns ();
55
+ }
56
+
57
+ return IRQ_HANDLED ;
58
+ }
59
+
60
+ static const struct max16150_chip_info max16150_variant_a = {
61
+ .has_clr_gpio = true,
62
+ };
63
+
64
+ static const struct max16150_chip_info max16150_variant_b = {
65
+ .has_clr_gpio = false,
66
+ };
67
+
68
+ static int max16150_probe (struct platform_device * pdev )
69
+ {
70
+ const struct max16150_chip_info * chip_info ;
71
+ struct max16150_device * max16150 ;
72
+ int err ;
73
+ u32 keycode ;
74
+
75
+ chip_info = of_device_get_match_data (& pdev -> dev );
76
+ if (!chip_info )
77
+ return - EINVAL ;
78
+
79
+ max16150 = devm_kzalloc (& pdev -> dev , sizeof (* max16150 ), GFP_KERNEL );
80
+ if (!max16150 )
81
+ return - ENOMEM ;
82
+
83
+ max16150 -> chip_info = chip_info ;
84
+
85
+ max16150 -> input = devm_input_allocate_device (& pdev -> dev );
86
+ if (!max16150 -> input )
87
+ return - ENOMEM ;
88
+
89
+ max16150 -> input -> name = "MAX16150 Pushbutton" ;
90
+ max16150 -> input -> phys = "max16150/input0" ;
91
+ max16150 -> input -> id .bustype = BUS_HOST ;
92
+
93
+ if (device_property_read_u32 (& pdev -> dev , "linux,code" , & keycode ))
94
+ keycode = KEY_WAKEUP ;
95
+
96
+ max16150 -> keycode = keycode ;
97
+
98
+ input_set_capability (max16150 -> input , EV_KEY , max16150 -> keycode );
99
+
100
+ max16150 -> gpiod = devm_gpiod_get (& pdev -> dev , "interrupt" , GPIOD_IN );
101
+ if (IS_ERR (max16150 -> gpiod ))
102
+ return PTR_ERR (max16150 -> gpiod );
103
+
104
+ if (chip_info -> has_clr_gpio ) {
105
+ max16150 -> clr_gpiod = devm_gpiod_get (& pdev -> dev , "clr" , GPIOD_OUT_HIGH );
106
+ if (IS_ERR (max16150 -> clr_gpiod )) {
107
+ max16150 -> clr_gpiod = NULL ;
108
+ dev_warn (& pdev -> dev ,
109
+ "clr GPIO undefined, clr pin will not be asserted\n" );
110
+ } else {
111
+ gpiod_set_value (max16150 -> clr_gpiod , 0 );
112
+ }
113
+ }
114
+
115
+ max16150 -> irq = gpiod_to_irq (max16150 -> gpiod );
116
+ if (max16150 -> irq < 0 )
117
+ return dev_err_probe (& pdev -> dev , max16150 -> irq ,
118
+ "MAX16150: Failed to map GPIO to IRQ" );
119
+
120
+ dev_dbg (& pdev -> dev , "MAX16150: Mapped GPIO to IRQ %d\n" , max16150 -> irq );
121
+
122
+ err = devm_request_irq (& pdev -> dev , max16150 -> irq , max16150_irq_handler ,
123
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING ,
124
+ "max16150_irq" , max16150 );
125
+ if (err )
126
+ return err ;
127
+
128
+ err = input_register_device (max16150 -> input );
129
+ if (err )
130
+ return err ;
131
+
132
+ dev_dbg (& pdev -> dev , "Driver initialized successfully\n" );
133
+
134
+ return 0 ;
135
+ }
136
+
137
+ static const struct of_device_id max16150_of_match [] = {
138
+ { .compatible = "adi,max16150a" , .data = & max16150_variant_a },
139
+ { .compatible = "adi,max16150b" , .data = & max16150_variant_b },
140
+ { .compatible = "adi,max16150c" , .data = & max16150_variant_b },
141
+ { .compatible = "adi,max16169a" , .data = & max16150_variant_a },
142
+ { .compatible = "adi,max16169b" , .data = & max16150_variant_b },
143
+ { .compatible = "adi,max16169c" , .data = & max16150_variant_b },
144
+ { }
145
+ };
146
+ MODULE_DEVICE_TABLE (of , max16150_of_match );
147
+
148
+ static struct platform_driver max16150_driver = {
149
+ .probe = max16150_probe ,
150
+ .driver = {
151
+ .name = "max16150" ,
152
+ .of_match_table = max16150_of_match ,
153
+ },
154
+ };
155
+ module_platform_driver (max16150_driver );
156
+
157
+ MODULE_AUTHOR ("Marc Paolo Sosa <marcpaolo.sosa@analog.com>" );
158
+ MODULE_DESCRIPTION ("MAX16150/MAX16169 Pushbutton Driver" );
159
+ MODULE_LICENSE ("GPL" );
160
+
0 commit comments