aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/input/mouse/Kconfig1
-rw-r--r--drivers/input/mouse/cyapa_gen5.c391
2 files changed, 392 insertions, 0 deletions
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 2541bfa1181a..f8c29d7dc478 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -216,6 +216,7 @@ config MOUSE_BCM5974
216config MOUSE_CYAPA 216config MOUSE_CYAPA
217 tristate "Cypress APA I2C Trackpad support" 217 tristate "Cypress APA I2C Trackpad support"
218 depends on I2C 218 depends on I2C
219 select CRC_ITU_T
219 help 220 help
220 This driver adds support for Cypress All Points Addressable (APA) 221 This driver adds support for Cypress All Points Addressable (APA)
221 I2C Trackpads, including the ones used in 2012 Samsung Chromebooks. 222 I2C Trackpads, including the ones used in 2012 Samsung Chromebooks.
diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c
index a049ae383027..442b29d87ed2 100644
--- a/drivers/input/mouse/cyapa_gen5.c
+++ b/drivers/input/mouse/cyapa_gen5.c
@@ -18,6 +18,7 @@
18#include <linux/completion.h> 18#include <linux/completion.h>
19#include <linux/slab.h> 19#include <linux/slab.h>
20#include <linux/unaligned/access_ok.h> 20#include <linux/unaligned/access_ok.h>
21#include <linux/crc-itu-t.h>
21#include "cyapa.h" 22#include "cyapa.h"
22 23
23 24
@@ -264,6 +265,79 @@ struct cyapa_gen5_report_data {
264 struct cyapa_gen5_touch_record touch_records[10]; 265 struct cyapa_gen5_touch_record touch_records[10];
265} __packed; 266} __packed;
266 267
268struct cyapa_tsg_bin_image_head {
269 u8 head_size; /* Unit: bytes, including itself. */
270 u8 ttda_driver_major_version; /* Reserved as 0. */
271 u8 ttda_driver_minor_version; /* Reserved as 0. */
272 u8 fw_major_version;
273 u8 fw_minor_version;
274 u8 fw_revision_control_number[8];
275} __packed;
276
277struct cyapa_tsg_bin_image_data_record {
278 u8 flash_array_id;
279 __be16 row_number;
280 /* The number of bytes of flash data contained in this record. */
281 __be16 record_len;
282 /* The flash program data. */
283 u8 record_data[CYAPA_TSG_FW_ROW_SIZE];
284} __packed;
285
286struct cyapa_tsg_bin_image {
287 struct cyapa_tsg_bin_image_head image_head;
288 struct cyapa_tsg_bin_image_data_record records[0];
289} __packed;
290
291struct gen5_bl_packet_start {
292 u8 sop; /* Start of packet, must be 01h */
293 u8 cmd_code;
294 __le16 data_length; /* Size of data parameter start from data[0] */
295} __packed;
296
297struct gen5_bl_packet_end {
298 __le16 crc;
299 u8 eop; /* End of packet, must be 17h */
300} __packed;
301
302struct gen5_bl_cmd_head {
303 __le16 addr; /* Output report register address, must be 0004h */
304 /* Size of packet not including output report register address */
305 __le16 length;
306 u8 report_id; /* Bootloader output report id, must be 40h */
307 u8 rsvd; /* Reserved, must be 0 */
308 struct gen5_bl_packet_start packet_start;
309 u8 data[0]; /* Command data variable based on commands */
310} __packed;
311
312/* Initiate bootload command data structure. */
313struct gen5_bl_initiate_cmd_data {
314 /* Key must be "A5h 01h 02h 03h FFh FEh FDh 5Ah" */
315 u8 key[CYAPA_TSG_BL_KEY_SIZE];
316 u8 metadata_raw_parameter[CYAPA_TSG_FLASH_MAP_METADATA_SIZE];
317 __le16 metadata_crc;
318} __packed;
319
320struct gen5_bl_metadata_row_params {
321 __le16 size;
322 __le16 maximun_size;
323 __le32 app_start;
324 __le16 app_len;
325 __le16 app_crc;
326 __le32 app_entry;
327 __le32 upgrade_start;
328 __le16 upgrade_len;
329 __le16 entry_row_crc;
330 u8 padding[36]; /* Padding data must be 0 */
331 __le16 metadata_crc; /* CRC starts at offset of 60 */
332} __packed;
333
334/* Bootload program and verify row command data structure */
335struct gen5_bl_flash_row_head {
336 u8 flash_array_id;
337 __le16 flash_row_id;
338 u8 flash_data[0];
339} __packed;
340
267struct gen5_app_cmd_head { 341struct gen5_app_cmd_head {
268 __le16 addr; /* Output report register address, must be 0004h */ 342 __le16 addr; /* Output report register address, must be 0004h */
269 /* Size of packet not including output report register address */ 343 /* Size of packet not including output report register address */
@@ -297,6 +371,10 @@ struct gen5_app_get_parameter_data {
297#define GEN5_DEV_UNINIT_SLEEP_TIME(cyapa) \ 371#define GEN5_DEV_UNINIT_SLEEP_TIME(cyapa) \
298 (((cyapa)->dev_sleep_time) == UNINIT_SLEEP_TIME) 372 (((cyapa)->dev_sleep_time) == UNINIT_SLEEP_TIME)
299 373
374
375static u8 cyapa_gen5_bl_cmd_key[] = { 0xa5, 0x01, 0x02, 0x03,
376 0xff, 0xfe, 0xfd, 0x5a };
377
300static int cyapa_gen5_initialize(struct cyapa *cyapa) 378static int cyapa_gen5_initialize(struct cyapa *cyapa)
301{ 379{
302 struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5; 380 struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5;
@@ -618,6 +696,22 @@ static bool cyapa_gen5_sort_tsg_pip_app_resp_data(struct cyapa *cyapa,
618 return false; 696 return false;
619} 697}
620 698
699static bool cyapa_gen5_sort_application_launch_data(struct cyapa *cyapa,
700 u8 *buf, int len)
701{
702 if (buf == NULL || len < GEN5_RESP_LENGTH_SIZE)
703 return false;
704
705 /*
706 * After reset or power on, trackpad device always sets to 0x00 0x00
707 * to indicate a reset or power on event.
708 */
709 if (buf[0] == 0 && buf[1] == 0)
710 return true;
711
712 return false;
713}
714
621static bool cyapa_gen5_sort_hid_descriptor_data(struct cyapa *cyapa, 715static bool cyapa_gen5_sort_hid_descriptor_data(struct cyapa *cyapa,
622 u8 *buf, int len) 716 u8 *buf, int len)
623{ 717{
@@ -923,6 +1017,80 @@ static int cyapa_gen5_state_parse(struct cyapa *cyapa, u8 *reg_data, int len)
923 return -EAGAIN; 1017 return -EAGAIN;
924} 1018}
925 1019
1020static int cyapa_gen5_bl_initiate(struct cyapa *cyapa,
1021 const struct firmware *fw)
1022{
1023 struct cyapa_tsg_bin_image *image;
1024 struct gen5_bl_cmd_head *bl_cmd_head;
1025 struct gen5_bl_packet_start *bl_packet_start;
1026 struct gen5_bl_initiate_cmd_data *cmd_data;
1027 struct gen5_bl_packet_end *bl_packet_end;
1028 u8 cmd[CYAPA_TSG_MAX_CMD_SIZE];
1029 int cmd_len;
1030 u16 cmd_data_len;
1031 u16 cmd_crc = 0;
1032 u16 meta_data_crc = 0;
1033 u8 resp_data[11];
1034 int resp_len;
1035 int records_num;
1036 u8 *data;
1037 int error;
1038
1039 /* Try to dump all buffered report data before any send command. */
1040 cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
1041
1042 memset(cmd, 0, CYAPA_TSG_MAX_CMD_SIZE);
1043 bl_cmd_head = (struct gen5_bl_cmd_head *)cmd;
1044 cmd_data_len = CYAPA_TSG_BL_KEY_SIZE + CYAPA_TSG_FLASH_MAP_BLOCK_SIZE;
1045 cmd_len = sizeof(struct gen5_bl_cmd_head) + cmd_data_len +
1046 sizeof(struct gen5_bl_packet_end);
1047
1048 put_unaligned_le16(GEN5_OUTPUT_REPORT_ADDR, &bl_cmd_head->addr);
1049 put_unaligned_le16(cmd_len - 2, &bl_cmd_head->length);
1050 bl_cmd_head->report_id = GEN5_BL_CMD_REPORT_ID;
1051
1052 bl_packet_start = &bl_cmd_head->packet_start;
1053 bl_packet_start->sop = GEN5_SOP_KEY;
1054 bl_packet_start->cmd_code = GEN5_BL_CMD_INITIATE_BL;
1055 /* 8 key bytes and 128 bytes block size */
1056 put_unaligned_le16(cmd_data_len, &bl_packet_start->data_length);
1057
1058 cmd_data = (struct gen5_bl_initiate_cmd_data *)bl_cmd_head->data;
1059 memcpy(cmd_data->key, cyapa_gen5_bl_cmd_key, CYAPA_TSG_BL_KEY_SIZE);
1060
1061 /* Copy 60 bytes Meta Data Row Parameters */
1062 image = (struct cyapa_tsg_bin_image *)fw->data;
1063 records_num = (fw->size - sizeof(struct cyapa_tsg_bin_image_head)) /
1064 sizeof(struct cyapa_tsg_bin_image_data_record);
1065 /* APP_INTEGRITY row is always the last row block */
1066 data = image->records[records_num - 1].record_data;
1067 memcpy(cmd_data->metadata_raw_parameter, data,
1068 CYAPA_TSG_FLASH_MAP_METADATA_SIZE);
1069
1070 meta_data_crc = crc_itu_t(0xffff, cmd_data->metadata_raw_parameter,
1071 CYAPA_TSG_FLASH_MAP_METADATA_SIZE);
1072 put_unaligned_le16(meta_data_crc, &cmd_data->metadata_crc);
1073
1074 bl_packet_end = (struct gen5_bl_packet_end *)(bl_cmd_head->data +
1075 cmd_data_len);
1076 cmd_crc = crc_itu_t(0xffff, (u8 *)bl_packet_start,
1077 sizeof(struct gen5_bl_packet_start) + cmd_data_len);
1078 put_unaligned_le16(cmd_crc, &bl_packet_end->crc);
1079 bl_packet_end->eop = GEN5_EOP_KEY;
1080
1081 resp_len = sizeof(resp_data);
1082 error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
1083 cmd, cmd_len,
1084 resp_data, &resp_len, 12000,
1085 cyapa_gen5_sort_tsg_pip_bl_resp_data, true);
1086 if (error || resp_len != GEN5_BL_INITIATE_RESP_LEN ||
1087 resp_data[2] != GEN5_BL_RESP_REPORT_ID ||
1088 !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]))
1089 return error ? error : -EAGAIN;
1090
1091 return 0;
1092}
1093
926static bool cyapa_gen5_sort_bl_exit_data(struct cyapa *cyapa, u8 *buf, int len) 1094static bool cyapa_gen5_sort_bl_exit_data(struct cyapa *cyapa, u8 *buf, int len)
927{ 1095{
928 if (buf == NULL || len < GEN5_RESP_LENGTH_SIZE) 1096 if (buf == NULL || len < GEN5_RESP_LENGTH_SIZE)
@@ -973,6 +1141,219 @@ static int cyapa_gen5_bl_exit(struct cyapa *cyapa)
973 return -ENODEV; 1141 return -ENODEV;
974} 1142}
975 1143
1144static int cyapa_gen5_bl_enter(struct cyapa *cyapa)
1145{
1146 u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2F, 0x00, 0x01 };
1147 u8 resp_data[2];
1148 int resp_len;
1149 int error;
1150
1151 error = cyapa_poll_state(cyapa, 500);
1152 if (error < 0)
1153 return error;
1154 if (cyapa->gen != CYAPA_GEN5)
1155 return -EINVAL;
1156
1157 /* Already in Gen5 BL. Skipping exit. */
1158 if (cyapa->state == CYAPA_STATE_GEN5_BL)
1159 return 0;
1160
1161 if (cyapa->state != CYAPA_STATE_GEN5_APP)
1162 return -EAGAIN;
1163
1164 /* Try to dump all buffered report data before any send command. */
1165 cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
1166
1167 /*
1168 * Send bootloader enter command to trackpad device,
1169 * after enter bootloader, the response data is two bytes of 0x00 0x00.
1170 */
1171 resp_len = sizeof(resp_data);
1172 memset(resp_data, 0, resp_len);
1173 error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
1174 cmd, sizeof(cmd),
1175 resp_data, &resp_len,
1176 5000, cyapa_gen5_sort_application_launch_data,
1177 true);
1178 if (error || resp_data[0] != 0x00 || resp_data[1] != 0x00)
1179 return error < 0 ? error : -EAGAIN;
1180
1181 cyapa->operational = false;
1182 cyapa->state = CYAPA_STATE_GEN5_BL;
1183 return 0;
1184}
1185
1186static int cyapa_gen5_check_fw(struct cyapa *cyapa, const struct firmware *fw)
1187{
1188 struct device *dev = &cyapa->client->dev;
1189 struct gen5_bl_metadata_row_params metadata;
1190 struct cyapa_tsg_bin_image *image;
1191 int flash_records_count;
1192 u16 app_crc = 0;
1193 u16 app_integrity_crc = 0;
1194 u16 row_num;
1195 u8 *data;
1196 int record_index;
1197 int i;
1198
1199 image = (struct cyapa_tsg_bin_image *)fw->data;
1200 flash_records_count = (fw->size -
1201 sizeof(struct cyapa_tsg_bin_image_head)) /
1202 sizeof(struct cyapa_tsg_bin_image_data_record);
1203
1204 /* APP_INTEGRITY row is always the last row block,
1205 * and the row id must be 0x01ff */
1206 row_num = get_unaligned_be16(
1207 &image->records[flash_records_count - 1].row_number);
1208 if (image->records[flash_records_count - 1].flash_array_id != 0x00 &&
1209 row_num != 0x01ff) {
1210 dev_err(dev, "%s: invalid app_integrity data.\n", __func__);
1211 return -EINVAL;
1212 }
1213 data = image->records[flash_records_count - 1].record_data;
1214
1215 metadata.app_start = get_unaligned_le32(&data[4]);
1216 metadata.app_len = get_unaligned_le16(&data[8]);
1217 metadata.app_crc = get_unaligned_le16(&data[10]);
1218 metadata.upgrade_start = get_unaligned_le32(&data[16]);
1219 metadata.upgrade_len = get_unaligned_le16(&data[20]);
1220 metadata.metadata_crc = get_unaligned_le16(&data[60]);
1221
1222 if ((metadata.app_start + metadata.app_len +
1223 metadata.upgrade_start + metadata.upgrade_len) %
1224 CYAPA_TSG_FW_ROW_SIZE) {
1225 dev_err(dev, "%s: invalid image alignment.\n", __func__);
1226 return -EINVAL;
1227 }
1228
1229 /* Verify app_integrity crc */
1230 app_integrity_crc = crc_itu_t(0xffff, data,
1231 CYAPA_TSG_APP_INTEGRITY_SIZE);
1232 if (app_integrity_crc != metadata.metadata_crc) {
1233 dev_err(dev, "%s: invalid app_integrity crc.\n", __func__);
1234 return -EINVAL;
1235 }
1236
1237 /*
1238 * Verify application image CRC
1239 */
1240 record_index = metadata.app_start / CYAPA_TSG_FW_ROW_SIZE -
1241 CYAPA_TSG_IMG_START_ROW_NUM;
1242 data = (u8 *)&image->records[record_index].record_data;
1243 app_crc = crc_itu_t(0xffff, data, CYAPA_TSG_FW_ROW_SIZE);
1244 for (i = 1; i < (metadata.app_len / CYAPA_TSG_FW_ROW_SIZE); i++) {
1245 data = (u8 *)&image->records[++record_index].record_data;
1246 app_crc = crc_itu_t(app_crc, data, CYAPA_TSG_FW_ROW_SIZE);
1247 }
1248
1249 if (app_crc != metadata.app_crc) {
1250 dev_err(dev, "%s: invalid firmware app crc check.\n", __func__);
1251 return -EINVAL;
1252 }
1253
1254 return 0;
1255}
1256
1257static int cyapa_gen5_write_fw_block(struct cyapa *cyapa,
1258 struct cyapa_tsg_bin_image_data_record *flash_record)
1259{
1260 struct gen5_bl_cmd_head *bl_cmd_head;
1261 struct gen5_bl_packet_start *bl_packet_start;
1262 struct gen5_bl_flash_row_head *flash_row_head;
1263 struct gen5_bl_packet_end *bl_packet_end;
1264 u8 cmd[CYAPA_TSG_MAX_CMD_SIZE];
1265 u16 cmd_len;
1266 u8 flash_array_id;
1267 u16 flash_row_id;
1268 u16 record_len;
1269 u8 *record_data;
1270 u16 data_len;
1271 u16 crc;
1272 u8 resp_data[11];
1273 int resp_len;
1274 int error;
1275
1276 flash_array_id = flash_record->flash_array_id;
1277 flash_row_id = get_unaligned_be16(&flash_record->row_number);
1278 record_len = get_unaligned_be16(&flash_record->record_len);
1279 record_data = flash_record->record_data;
1280
1281 memset(cmd, 0, CYAPA_TSG_MAX_CMD_SIZE);
1282 bl_cmd_head = (struct gen5_bl_cmd_head *)cmd;
1283 bl_packet_start = &bl_cmd_head->packet_start;
1284 cmd_len = sizeof(struct gen5_bl_cmd_head) +
1285 sizeof(struct gen5_bl_flash_row_head) +
1286 CYAPA_TSG_FLASH_MAP_BLOCK_SIZE +
1287 sizeof(struct gen5_bl_packet_end);
1288
1289 put_unaligned_le16(GEN5_OUTPUT_REPORT_ADDR, &bl_cmd_head->addr);
1290 /* Don't include 2 bytes register address */
1291 put_unaligned_le16(cmd_len - 2, &bl_cmd_head->length);
1292 bl_cmd_head->report_id = GEN5_BL_CMD_REPORT_ID;
1293 bl_packet_start->sop = GEN5_SOP_KEY;
1294 bl_packet_start->cmd_code = GEN5_BL_CMD_PROGRAM_VERIFY_ROW;
1295
1296 /* 1 (Flash Array ID) + 2 (Flash Row ID) + 128 (flash data) */
1297 data_len = sizeof(struct gen5_bl_flash_row_head) + record_len;
1298 put_unaligned_le16(data_len, &bl_packet_start->data_length);
1299
1300 flash_row_head = (struct gen5_bl_flash_row_head *)bl_cmd_head->data;
1301 flash_row_head->flash_array_id = flash_array_id;
1302 put_unaligned_le16(flash_row_id, &flash_row_head->flash_row_id);
1303 memcpy(flash_row_head->flash_data, record_data, record_len);
1304
1305 bl_packet_end = (struct gen5_bl_packet_end *)(bl_cmd_head->data +
1306 data_len);
1307 crc = crc_itu_t(0xffff, (u8 *)bl_packet_start,
1308 sizeof(struct gen5_bl_packet_start) + data_len);
1309 put_unaligned_le16(crc, &bl_packet_end->crc);
1310 bl_packet_end->eop = GEN5_EOP_KEY;
1311
1312 resp_len = sizeof(resp_data);
1313 error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, cmd_len,
1314 resp_data, &resp_len,
1315 500, cyapa_gen5_sort_tsg_pip_bl_resp_data, true);
1316 if (error || resp_len != GEN5_BL_BLOCK_WRITE_RESP_LEN ||
1317 resp_data[2] != GEN5_BL_RESP_REPORT_ID ||
1318 !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]))
1319 return error < 0 ? error : -EAGAIN;
1320
1321 return 0;
1322}
1323
1324static int cyapa_gen5_do_fw_update(struct cyapa *cyapa,
1325 const struct firmware *fw)
1326{
1327 struct device *dev = &cyapa->client->dev;
1328 struct cyapa_tsg_bin_image_data_record *flash_record;
1329 struct cyapa_tsg_bin_image *image =
1330 (struct cyapa_tsg_bin_image *)fw->data;
1331 int flash_records_count;
1332 int i;
1333 int error;
1334
1335 cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
1336
1337 flash_records_count =
1338 (fw->size - sizeof(struct cyapa_tsg_bin_image_head)) /
1339 sizeof(struct cyapa_tsg_bin_image_data_record);
1340 /*
1341 * The last flash row 0x01ff has been written through bl_initiate
1342 * command, so DO NOT write flash 0x01ff to trackpad device.
1343 */
1344 for (i = 0; i < (flash_records_count - 1); i++) {
1345 flash_record = &image->records[i];
1346 error = cyapa_gen5_write_fw_block(cyapa, flash_record);
1347 if (error) {
1348 dev_err(dev, "%s: Gen5 FW update aborted: %d\n",
1349 __func__, error);
1350 return error;
1351 }
1352 }
1353
1354 return 0;
1355}
1356
976static int cyapa_gen5_change_power_state(struct cyapa *cyapa, u8 power_state) 1357static int cyapa_gen5_change_power_state(struct cyapa *cyapa, u8 power_state)
977{ 1358{
978 u8 cmd[8] = { 0x04, 0x00, 0x06, 0x00, 0x2f, 0x00, 0x08, 0x01 }; 1359 u8 cmd[8] = { 0x04, 0x00, 0x06, 0x00, 0x2f, 0x00, 0x08, 0x01 };
@@ -1665,7 +2046,17 @@ static int cyapa_gen5_irq_handler(struct cyapa *cyapa)
1665 return 0; 2046 return 0;
1666} 2047}
1667 2048
2049static int cyapa_gen5_bl_activate(struct cyapa *cyapa) { return 0; }
2050static int cyapa_gen5_bl_deactivate(struct cyapa *cyapa) { return 0; }
2051
1668const struct cyapa_dev_ops cyapa_gen5_ops = { 2052const struct cyapa_dev_ops cyapa_gen5_ops = {
2053 .check_fw = cyapa_gen5_check_fw,
2054 .bl_enter = cyapa_gen5_bl_enter,
2055 .bl_initiate = cyapa_gen5_bl_initiate,
2056 .update_fw = cyapa_gen5_do_fw_update,
2057 .bl_activate = cyapa_gen5_bl_activate,
2058 .bl_deactivate = cyapa_gen5_bl_deactivate,
2059
1669 .initialize = cyapa_gen5_initialize, 2060 .initialize = cyapa_gen5_initialize,
1670 2061
1671 .state_parse = cyapa_gen5_state_parse, 2062 .state_parse = cyapa_gen5_state_parse,