diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/input/mouse/Kconfig | 1 | ||||
-rw-r--r-- | drivers/input/mouse/cyapa_gen5.c | 391 |
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 | |||
216 | config MOUSE_CYAPA | 216 | config 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 | ||
268 | struct 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 | |||
277 | struct 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 | |||
286 | struct 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 | |||
291 | struct 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 | |||
297 | struct gen5_bl_packet_end { | ||
298 | __le16 crc; | ||
299 | u8 eop; /* End of packet, must be 17h */ | ||
300 | } __packed; | ||
301 | |||
302 | struct 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. */ | ||
313 | struct 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 | |||
320 | struct 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 */ | ||
335 | struct gen5_bl_flash_row_head { | ||
336 | u8 flash_array_id; | ||
337 | __le16 flash_row_id; | ||
338 | u8 flash_data[0]; | ||
339 | } __packed; | ||
340 | |||
267 | struct gen5_app_cmd_head { | 341 | struct 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 | |||
375 | static u8 cyapa_gen5_bl_cmd_key[] = { 0xa5, 0x01, 0x02, 0x03, | ||
376 | 0xff, 0xfe, 0xfd, 0x5a }; | ||
377 | |||
300 | static int cyapa_gen5_initialize(struct cyapa *cyapa) | 378 | static 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 | ||
699 | static 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 | |||
621 | static bool cyapa_gen5_sort_hid_descriptor_data(struct cyapa *cyapa, | 715 | static 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 | ||
1020 | static 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 | |||
926 | static bool cyapa_gen5_sort_bl_exit_data(struct cyapa *cyapa, u8 *buf, int len) | 1094 | static 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 | ||
1144 | static 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 | |||
1186 | static 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 | |||
1257 | static 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 | |||
1324 | static 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 | |||
976 | static int cyapa_gen5_change_power_state(struct cyapa *cyapa, u8 power_state) | 1357 | static 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 | ||
2049 | static int cyapa_gen5_bl_activate(struct cyapa *cyapa) { return 0; } | ||
2050 | static int cyapa_gen5_bl_deactivate(struct cyapa *cyapa) { return 0; } | ||
2051 | |||
1668 | const struct cyapa_dev_ops cyapa_gen5_ops = { | 2052 | const 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, |