1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * qt2160.c - Atmel AT42QT2160 Touch Sense Controller 4 * 5 * Copyright (C) 2009 Raphael Derosso Pereira <raphaelpereira@gmail.com> 6 */ 7 8#include <linux/kernel.h> 9#include <linux/leds.h> 10#include <linux/module.h> 11#include <linux/slab.h> 12#include <linux/jiffies.h> 13#include <linux/i2c.h> 14#include <linux/irq.h> 15#include <linux/interrupt.h> 16#include <linux/input.h> 17 18#define QT2160_VALID_CHIPID 0x11 19 20#define QT2160_CMD_CHIPID 0 21#define QT2160_CMD_CODEVER 1 22#define QT2160_CMD_GSTAT 2 23#define QT2160_CMD_KEYS3 3 24#define QT2160_CMD_KEYS4 4 25#define QT2160_CMD_SLIDE 5 26#define QT2160_CMD_GPIOS 6 27#define QT2160_CMD_SUBVER 7 28#define QT2160_CMD_CALIBRATE 10 29#define QT2160_CMD_DRIVE_X 70 30#define QT2160_CMD_PWMEN_X 74 31#define QT2160_CMD_PWM_DUTY 76 32 33#define QT2160_NUM_LEDS_X 8 34 35#define QT2160_CYCLE_INTERVAL 2000 /* msec - 2 sec */ 36 37static unsigned char qt2160_key2code[] = { 38 KEY_0, KEY_1, KEY_2, KEY_3, 39 KEY_4, KEY_5, KEY_6, KEY_7, 40 KEY_8, KEY_9, KEY_A, KEY_B, 41 KEY_C, KEY_D, KEY_E, KEY_F, 42}; 43 44#ifdef CONFIG_LEDS_CLASS 45struct qt2160_led { 46 struct qt2160_data *qt2160; 47 struct led_classdev cdev; 48 char name[32]; 49 int id; 50 enum led_brightness brightness; 51}; 52#endif 53 54struct qt2160_data { 55 struct i2c_client *client; 56 struct input_dev *input; 57 unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)]; 58 u16 key_matrix; 59#ifdef CONFIG_LEDS_CLASS 60 struct qt2160_led leds[QT2160_NUM_LEDS_X]; 61#endif 62}; 63 64static int qt2160_read(struct i2c_client *client, u8 reg); 65static int qt2160_write(struct i2c_client *client, u8 reg, u8 data); 66 67#ifdef CONFIG_LEDS_CLASS 68 69static int qt2160_led_set(struct led_classdev *cdev, 70 enum led_brightness value) 71{ 72 struct qt2160_led *led = container_of(cdev, struct qt2160_led, cdev); 73 struct qt2160_data *qt2160 = led->qt2160; 74 struct i2c_client *client = qt2160->client; 75 u32 drive, pwmen; 76 77 if (value != led->brightness) { 78 drive = qt2160_read(client, QT2160_CMD_DRIVE_X); 79 pwmen = qt2160_read(client, QT2160_CMD_PWMEN_X); 80 if (value != LED_OFF) { 81 drive |= BIT(led->id); 82 pwmen |= BIT(led->id); 83 84 } else { 85 drive &= ~BIT(led->id); 86 pwmen &= ~BIT(led->id); 87 } 88 qt2160_write(client, QT2160_CMD_DRIVE_X, drive); 89 qt2160_write(client, QT2160_CMD_PWMEN_X, pwmen); 90 91 /* 92 * Changing this register will change the brightness 93 * of every LED in the qt2160. It's a HW limitation. 94 */ 95 if (value != LED_OFF) 96 qt2160_write(client, QT2160_CMD_PWM_DUTY, value); 97 98 led->brightness = value; 99 } 100 101 return 0; 102} 103 104#endif /* CONFIG_LEDS_CLASS */ 105 106static int qt2160_read_block(struct i2c_client *client, 107 u8 inireg, u8 *buffer, unsigned int count) 108{ 109 int error, idx = 0; 110 111 /* 112 * Can't use SMBus block data read. Check for I2C functionality to speed 113 * things up whenever possible. Otherwise we will be forced to read 114 * sequentially. 115 */ 116 if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { 117 118 error = i2c_smbus_write_byte(client, inireg + idx); 119 if (error) { 120 dev_err(&client->dev, 121 "couldn't send request. Returned %d\n", error); 122 return error; 123 } 124 125 error = i2c_master_recv(client, buffer, count); 126 if (error != count) { 127 dev_err(&client->dev, 128 "couldn't read registers. Returned %d bytes\n", error); 129 return error; 130 } 131 } else { 132 133 while (count--) { 134 int data; 135 136 error = i2c_smbus_write_byte(client, inireg + idx); 137 if (error) { 138 dev_err(&client->dev, 139 "couldn't send request. Returned %d\n", error); 140 return error; 141 } 142 143 data = i2c_smbus_read_byte(client); 144 if (data < 0) { 145 dev_err(&client->dev, 146 "couldn't read register. Returned %d\n", data); 147 return data; 148 } 149 150 buffer[idx++] = data; 151 } 152 } 153 154 return 0; 155} 156 157static void qt2160_get_key_matrix(struct input_dev *input) 158{ 159 struct qt2160_data *qt2160 = input_get_drvdata(input); 160 struct i2c_client *client = qt2160->client; 161 u8 regs[6]; 162 u16 old_matrix, new_matrix; 163 int ret, i, mask; 164 165 dev_dbg(&client->dev, "requesting keys...\n"); 166 167 /* 168 * Read all registers from General Status Register 169 * to GPIOs register 170 */ 171 ret = qt2160_read_block(client, QT2160_CMD_GSTAT, regs, 6); 172 if (ret) { 173 dev_err(&client->dev, 174 "could not perform chip read.\n"); 175 return; 176 } 177 178 old_matrix = qt2160->key_matrix; 179 qt2160->key_matrix = new_matrix = (regs[2] << 8) | regs[1]; 180 181 mask = 0x01; 182 for (i = 0; i < 16; ++i, mask <<= 1) { 183 int keyval = new_matrix & mask; 184 185 if ((old_matrix & mask) != keyval) { 186 input_report_key(input, qt2160->keycodes[i], keyval); 187 dev_dbg(&client->dev, "key %d %s\n", 188 i, keyval ? "pressed" : "released"); 189 } 190 } 191 192 input_sync(input); 193} 194 195static irqreturn_t qt2160_irq(int irq, void *data) 196{ 197 struct input_dev *input = data; 198 199 qt2160_get_key_matrix(input); 200 201 return IRQ_HANDLED; 202} 203 204static int qt2160_read(struct i2c_client *client, u8 reg) 205{ 206 int ret; 207 208 ret = i2c_smbus_write_byte(client, reg); 209 if (ret) { 210 dev_err(&client->dev, 211 "couldn't send request. Returned %d\n", ret); 212 return ret; 213 } 214 215 ret = i2c_smbus_read_byte(client); 216 if (ret < 0) { 217 dev_err(&client->dev, 218 "couldn't read register. Returned %d\n", ret); 219 return ret; 220 } 221 222 return ret; 223} 224 225static int qt2160_write(struct i2c_client *client, u8 reg, u8 data) 226{ 227 int ret; 228 229 ret = i2c_smbus_write_byte_data(client, reg, data); 230 if (ret < 0) 231 dev_err(&client->dev, 232 "couldn't write data. Returned %d\n", ret); 233 234 return ret; 235} 236 237#ifdef CONFIG_LEDS_CLASS 238 239static int qt2160_register_leds(struct qt2160_data *qt2160) 240{ 241 struct i2c_client *client = qt2160->client; 242 int error; 243 int i; 244 245 for (i = 0; i < QT2160_NUM_LEDS_X; i++) { 246 struct qt2160_led *led = &qt2160->leds[i]; 247 248 snprintf(led->name, sizeof(led->name), "qt2160:x%d", i); 249 led->cdev.name = led->name; 250 led->cdev.brightness_set_blocking = qt2160_led_set; 251 led->cdev.brightness = LED_OFF; 252 led->id = i; 253 led->qt2160 = qt2160; 254 255 error = devm_led_classdev_register(&client->dev, &led->cdev); 256 if (error) 257 return error; 258 } 259 260 /* Tur off LEDs */ 261 qt2160_write(client, QT2160_CMD_DRIVE_X, 0); 262 qt2160_write(client, QT2160_CMD_PWMEN_X, 0); 263 qt2160_write(client, QT2160_CMD_PWM_DUTY, 0); 264 265 return 0; 266} 267 268#else 269 270static inline int qt2160_register_leds(struct qt2160_data *qt2160) 271{ 272 return 0; 273} 274 275#endif 276 277static bool qt2160_identify(struct i2c_client *client) 278{ 279 int id, ver, rev; 280 281 /* Read Chid ID to check if chip is valid */ 282 id = qt2160_read(client, QT2160_CMD_CHIPID); 283 if (id != QT2160_VALID_CHIPID) { 284 dev_err(&client->dev, "ID %d not supported\n", id); 285 return false; 286 } 287 288 /* Read chip firmware version */ 289 ver = qt2160_read(client, QT2160_CMD_CODEVER); 290 if (ver < 0) { 291 dev_err(&client->dev, "could not get firmware version\n"); 292 return false; 293 } 294 295 /* Read chip firmware revision */ 296 rev = qt2160_read(client, QT2160_CMD_SUBVER); 297 if (rev < 0) { 298 dev_err(&client->dev, "could not get firmware revision\n"); 299 return false; 300 } 301 302 dev_info(&client->dev, "AT42QT2160 firmware version %d.%d.%d\n", 303 ver >> 4, ver & 0xf, rev); 304 305 return true; 306} 307 308static int qt2160_probe(struct i2c_client *client) 309{ 310 struct qt2160_data *qt2160; 311 struct input_dev *input; 312 int i; 313 int error; 314 315 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { 316 dev_err(&client->dev, "%s adapter not supported\n", 317 dev_driver_string(&client->adapter->dev)); 318 return -ENODEV; 319 } 320 321 if (!qt2160_identify(client)) 322 return -ENODEV; 323 324 /* Chip is valid and active. Allocate structure */ 325 qt2160 = devm_kzalloc(&client->dev, sizeof(*qt2160), GFP_KERNEL); 326 if (!qt2160) 327 return -ENOMEM; 328 329 input = devm_input_allocate_device(&client->dev); 330 if (!input) 331 return -ENOMEM; 332 333 qt2160->client = client; 334 qt2160->input = input; 335 336 input->name = "AT42QT2160 Touch Sense Keyboard"; 337 input->id.bustype = BUS_I2C; 338 339 input->keycode = qt2160->keycodes; 340 input->keycodesize = sizeof(qt2160->keycodes[0]); 341 input->keycodemax = ARRAY_SIZE(qt2160_key2code); 342 343 __set_bit(EV_KEY, input->evbit); 344 __clear_bit(EV_REP, input->evbit); 345 for (i = 0; i < ARRAY_SIZE(qt2160_key2code); i++) { 346 qt2160->keycodes[i] = qt2160_key2code[i]; 347 __set_bit(qt2160_key2code[i], input->keybit); 348 } 349 __clear_bit(KEY_RESERVED, input->keybit); 350 351 input_set_drvdata(input, qt2160); 352 353 /* Calibrate device */ 354 error = qt2160_write(client, QT2160_CMD_CALIBRATE, 1); 355 if (error) { 356 dev_err(&client->dev, "failed to calibrate device\n"); 357 return error; 358 } 359 360 if (client->irq) { 361 error = devm_request_threaded_irq(&client->dev, client->irq, 362 NULL, qt2160_irq, 363 IRQF_ONESHOT, 364 "qt2160", input); 365 if (error) { 366 dev_err(&client->dev, 367 "failed to allocate irq %d\n", client->irq); 368 return error; 369 } 370 } else { 371 error = input_setup_polling(input, qt2160_get_key_matrix); 372 if (error) { 373 dev_err(&client->dev, "Failed to setup polling\n"); 374 return error; 375 } 376 input_set_poll_interval(input, QT2160_CYCLE_INTERVAL); 377 } 378 379 error = qt2160_register_leds(qt2160); 380 if (error) { 381 dev_err(&client->dev, "Failed to register leds\n"); 382 return error; 383 } 384 385 error = input_register_device(qt2160->input); 386 if (error) { 387 dev_err(&client->dev, 388 "Failed to register input device\n"); 389 return error; 390 } 391 392 return 0; 393} 394 395static const struct i2c_device_id qt2160_idtable[] = { 396 { "qt2160", 0, }, 397 { } 398}; 399 400MODULE_DEVICE_TABLE(i2c, qt2160_idtable); 401 402static struct i2c_driver qt2160_driver = { 403 .driver = { 404 .name = "qt2160", 405 }, 406 407 .id_table = qt2160_idtable, 408 .probe = qt2160_probe, 409}; 410 411module_i2c_driver(qt2160_driver); 412 413MODULE_AUTHOR("Raphael Derosso Pereira <raphaelpereira@gmail.com>"); 414MODULE_DESCRIPTION("Driver for AT42QT2160 Touch Sensor"); 415MODULE_LICENSE("GPL"); 416