1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) IBM Corporation 2020
7 #include <linux/init.h>
8 #include <linux/input.h>
9 #include <linux/kernel.h>
10 #include <linux/limits.h>
11 #include <linux/module.h>
13 #include <linux/spinlock.h>
15 #define DEVICE_NAME "ibm-panel"
16 #define PANEL_KEYCODES_COUNT 3
21 u32 keycodes
[PANEL_KEYCODES_COUNT
];
22 spinlock_t lock
; /* protects writes to idx and command */
23 struct input_dev
*input
;
26 static u8
ibm_panel_calculate_checksum(struct ibm_panel
*panel
)
32 for (i
= 0; i
< sizeof(panel
->command
) - 1; ++i
) {
33 sum
+= panel
->command
[i
];
47 static void ibm_panel_process_command(struct ibm_panel
*panel
)
52 if (panel
->command
[0] != 0xff && panel
->command
[1] != 0xf0) {
53 dev_dbg(&panel
->input
->dev
, "command invalid: %02x %02x\n",
54 panel
->command
[0], panel
->command
[1]);
58 chksum
= ibm_panel_calculate_checksum(panel
);
59 if (chksum
!= panel
->command
[sizeof(panel
->command
) - 1]) {
60 dev_dbg(&panel
->input
->dev
,
61 "command failed checksum: %u != %u\n", chksum
,
62 panel
->command
[sizeof(panel
->command
) - 1]);
66 button
= panel
->command
[2] & 0xf;
67 if (button
< PANEL_KEYCODES_COUNT
) {
68 input_report_key(panel
->input
, panel
->keycodes
[button
],
69 !(panel
->command
[2] & 0x80));
70 input_sync(panel
->input
);
72 dev_dbg(&panel
->input
->dev
, "unknown button %u\n",
77 static int ibm_panel_i2c_slave_cb(struct i2c_client
*client
,
78 enum i2c_slave_event event
, u8
*val
)
80 struct ibm_panel
*panel
= i2c_get_clientdata(client
);
82 dev_dbg(&panel
->input
->dev
, "event: %u data: %02x\n", event
, *val
);
84 guard(spinlock_irqsave
)(&panel
->lock
);
88 if (panel
->idx
== sizeof(panel
->command
))
89 ibm_panel_process_command(panel
);
91 dev_dbg(&panel
->input
->dev
,
92 "command incorrect size %u\n", panel
->idx
);
94 case I2C_SLAVE_WRITE_REQUESTED
:
97 case I2C_SLAVE_WRITE_RECEIVED
:
98 if (panel
->idx
< sizeof(panel
->command
))
99 panel
->command
[panel
->idx
++] = *val
;
102 * The command is too long and therefore invalid, so set the index
103 * to it's largest possible value. When a STOP is finally received,
104 * the command will be rejected upon processing.
108 case I2C_SLAVE_READ_REQUESTED
:
109 case I2C_SLAVE_READ_PROCESSED
:
119 static int ibm_panel_probe(struct i2c_client
*client
)
121 struct ibm_panel
*panel
;
125 panel
= devm_kzalloc(&client
->dev
, sizeof(*panel
), GFP_KERNEL
);
129 spin_lock_init(&panel
->lock
);
131 panel
->input
= devm_input_allocate_device(&client
->dev
);
135 panel
->input
->name
= client
->name
;
136 panel
->input
->id
.bustype
= BUS_I2C
;
138 error
= device_property_read_u32_array(&client
->dev
,
141 PANEL_KEYCODES_COUNT
);
144 * Use gamepad buttons as defaults for compatibility with
145 * existing applications.
147 panel
->keycodes
[0] = BTN_NORTH
;
148 panel
->keycodes
[1] = BTN_SOUTH
;
149 panel
->keycodes
[2] = BTN_SELECT
;
152 for (i
= 0; i
< PANEL_KEYCODES_COUNT
; ++i
)
153 input_set_capability(panel
->input
, EV_KEY
, panel
->keycodes
[i
]);
155 error
= input_register_device(panel
->input
);
157 dev_err(&client
->dev
,
158 "Failed to register input device: %d\n", error
);
162 i2c_set_clientdata(client
, panel
);
163 error
= i2c_slave_register(client
, ibm_panel_i2c_slave_cb
);
165 dev_err(&client
->dev
,
166 "Failed to register as i2c slave: %d\n", error
);
173 static void ibm_panel_remove(struct i2c_client
*client
)
175 i2c_slave_unregister(client
);
178 static const struct of_device_id ibm_panel_match
[] = {
179 { .compatible
= "ibm,op-panel" },
182 MODULE_DEVICE_TABLE(of
, ibm_panel_match
);
184 static struct i2c_driver ibm_panel_driver
= {
187 .of_match_table
= ibm_panel_match
,
189 .probe
= ibm_panel_probe
,
190 .remove
= ibm_panel_remove
,
192 module_i2c_driver(ibm_panel_driver
);
194 MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
195 MODULE_DESCRIPTION("IBM Operation Panel Driver");
196 MODULE_LICENSE("GPL");