Skip to content

Commit 63c5eb1

Browse files
smaeuldtor
authored andcommitted
Input: pinephone-keyboard - support the proxied I2C bus
The PinePhone keyboard case contains a battery managed by an integrated power bank IC. The power bank IC communicates over I2C, and the keyboard MCU firmware provides an interface to read and write its registers. Let's use this interface to implement a SMBus adapter, so we can reuse the driver for the power bank IC. Signed-off-by: Samuel Holland <samuel@sholland.org> Link: https://lore.kernel.org/r/20220618165747.55709-4-samuel@sholland.org Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
1 parent 1740696 commit 63c5eb1

File tree

1 file changed

+76
-0
lines changed

1 file changed

+76
-0
lines changed

drivers/input/keyboard/pinephone-keyboard.c

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
// Copyright (C) 2021-2022 Samuel Holland <samuel@sholland.org>
44

55
#include <linux/crc8.h>
6+
#include <linux/delay.h>
67
#include <linux/err.h>
78
#include <linux/i2c.h>
89
#include <linux/input.h>
910
#include <linux/input/matrix_keypad.h>
1011
#include <linux/interrupt.h>
1112
#include <linux/module.h>
1213
#include <linux/mod_devicetable.h>
14+
#include <linux/of.h>
1315
#include <linux/regulator/consumer.h>
1416
#include <linux/types.h>
1517

@@ -28,6 +30,11 @@
2830
#define PPKB_SCAN_DATA 0x08
2931
#define PPKB_SYS_CONFIG 0x20
3032
#define PPKB_SYS_CONFIG_DISABLE_SCAN BIT(0)
33+
#define PPKB_SYS_SMBUS_COMMAND 0x21
34+
#define PPKB_SYS_SMBUS_DATA 0x22
35+
#define PPKB_SYS_COMMAND 0x23
36+
#define PPKB_SYS_COMMAND_SMBUS_READ 0x91
37+
#define PPKB_SYS_COMMAND_SMBUS_WRITE 0xa1
3138

3239
#define PPKB_ROWS 6
3340
#define PPKB_COLS 12
@@ -136,6 +143,7 @@ static const struct matrix_keymap_data ppkb_keymap_data = {
136143
};
137144

138145
struct pinephone_keyboard {
146+
struct i2c_adapter adapter;
139147
struct input_dev *input;
140148
u8 buf[2][PPKB_BUF_LEN];
141149
u8 crc_table[CRC8_TABLE_SIZE];
@@ -144,6 +152,57 @@ struct pinephone_keyboard {
144152
bool fn_pressed;
145153
};
146154

155+
static int ppkb_adap_smbus_xfer(struct i2c_adapter *adap, u16 addr,
156+
unsigned short flags, char read_write,
157+
u8 command, int size,
158+
union i2c_smbus_data *data)
159+
{
160+
struct i2c_client *client = adap->algo_data;
161+
u8 buf[3];
162+
int ret;
163+
164+
buf[0] = command;
165+
buf[1] = data->byte;
166+
buf[2] = read_write == I2C_SMBUS_READ ? PPKB_SYS_COMMAND_SMBUS_READ
167+
: PPKB_SYS_COMMAND_SMBUS_WRITE;
168+
169+
ret = i2c_smbus_write_i2c_block_data(client, PPKB_SYS_SMBUS_COMMAND,
170+
sizeof(buf), buf);
171+
if (ret)
172+
return ret;
173+
174+
/* Read back the command status until it passes or fails. */
175+
do {
176+
usleep_range(300, 500);
177+
ret = i2c_smbus_read_byte_data(client, PPKB_SYS_COMMAND);
178+
} while (ret == buf[2]);
179+
if (ret < 0)
180+
return ret;
181+
/* Commands return 0x00 on success and 0xff on failure. */
182+
if (ret)
183+
return -EIO;
184+
185+
if (read_write == I2C_SMBUS_READ) {
186+
ret = i2c_smbus_read_byte_data(client, PPKB_SYS_SMBUS_DATA);
187+
if (ret < 0)
188+
return ret;
189+
190+
data->byte = ret;
191+
}
192+
193+
return 0;
194+
}
195+
196+
static u32 ppkg_adap_functionality(struct i2c_adapter *adap)
197+
{
198+
return I2C_FUNC_SMBUS_BYTE_DATA;
199+
}
200+
201+
static const struct i2c_algorithm ppkb_adap_algo = {
202+
.smbus_xfer = ppkb_adap_smbus_xfer,
203+
.functionality = ppkg_adap_functionality,
204+
};
205+
147206
static void ppkb_update(struct i2c_client *client)
148207
{
149208
struct pinephone_keyboard *ppkb = i2c_get_clientdata(client);
@@ -271,6 +330,7 @@ static int ppkb_probe(struct i2c_client *client)
271330
struct pinephone_keyboard *ppkb;
272331
struct regulator *vbat_supply;
273332
u8 info[PPKB_MATRIX_SIZE + 1];
333+
struct device_node *i2c_bus;
274334
int ret;
275335
int error;
276336

@@ -330,6 +390,22 @@ static int ppkb_probe(struct i2c_client *client)
330390

331391
i2c_set_clientdata(client, ppkb);
332392

393+
i2c_bus = of_get_child_by_name(dev->of_node, "i2c");
394+
if (i2c_bus) {
395+
ppkb->adapter.owner = THIS_MODULE;
396+
ppkb->adapter.algo = &ppkb_adap_algo;
397+
ppkb->adapter.algo_data = client;
398+
ppkb->adapter.dev.parent = dev;
399+
ppkb->adapter.dev.of_node = i2c_bus;
400+
strscpy(ppkb->adapter.name, DRV_NAME, sizeof(ppkb->adapter.name));
401+
402+
error = devm_i2c_add_adapter(dev, &ppkb->adapter);
403+
if (error) {
404+
dev_err(dev, "Failed to add I2C adapter: %d\n", error);
405+
return error;
406+
}
407+
}
408+
333409
crc8_populate_msb(ppkb->crc_table, PPKB_CRC8_POLYNOMIAL);
334410

335411
ppkb->input = devm_input_allocate_device(dev);

0 commit comments

Comments
 (0)