1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Goodix Touchscreen firmware upload support |
4 | * |
5 | * Copyright (c) 2021 Hans de Goede <hdegoede@redhat.com> |
6 | * |
7 | * This is a rewrite of gt9xx_update.c from the Allwinner H3 BSP which is: |
8 | * Copyright (c) 2010 - 2012 Goodix Technology. |
9 | * Author: andrew@goodix.com |
10 | */ |
11 | |
12 | #include <linux/device.h> |
13 | #include <linux/firmware.h> |
14 | #include <linux/i2c.h> |
15 | #include "goodix.h" |
16 | |
17 | #define sizeof(struct goodix_fw_header) |
18 | #define GOODIX_FW_SECTION_LENGTH 0x2000 |
19 | #define GOODIX_FW_DSP_LENGTH 0x1000 |
20 | #define GOODIX_FW_UPLOAD_ADDRESS 0xc000 |
21 | |
22 | #define GOODIX_CFG_LOC_HAVE_KEY 7 |
23 | #define GOODIX_CFG_LOC_DRVA_NUM 27 |
24 | #define GOODIX_CFG_LOC_DRVB_NUM 28 |
25 | #define GOODIX_CFG_LOC_SENS_NUM 29 |
26 | |
27 | struct { |
28 | u8 [4]; |
29 | u8 [8]; |
30 | u8 [2]; |
31 | } __packed; |
32 | |
33 | static u16 goodix_firmware_checksum(const u8 *data, int size) |
34 | { |
35 | u16 checksum = 0; |
36 | int i; |
37 | |
38 | for (i = 0; i < size; i += 2) |
39 | checksum += (data[i] << 8) + data[i + 1]; |
40 | |
41 | return checksum; |
42 | } |
43 | |
44 | static int goodix_firmware_verify(struct device *dev, const struct firmware *fw) |
45 | { |
46 | const struct goodix_fw_header *; |
47 | size_t expected_size; |
48 | const u8 *data; |
49 | u16 checksum; |
50 | char buf[9]; |
51 | |
52 | expected_size = GOODIX_FW_HEADER_LENGTH + 4 * GOODIX_FW_SECTION_LENGTH + |
53 | GOODIX_FW_DSP_LENGTH; |
54 | if (fw->size != expected_size) { |
55 | dev_err(dev, "Firmware has wrong size, expected %zu got %zu\n" , |
56 | expected_size, fw->size); |
57 | return -EINVAL; |
58 | } |
59 | |
60 | data = fw->data + GOODIX_FW_HEADER_LENGTH; |
61 | checksum = goodix_firmware_checksum(data, size: 4 * GOODIX_FW_SECTION_LENGTH); |
62 | if (checksum) { |
63 | dev_err(dev, "Main firmware checksum error\n" ); |
64 | return -EINVAL; |
65 | } |
66 | |
67 | data += 4 * GOODIX_FW_SECTION_LENGTH; |
68 | checksum = goodix_firmware_checksum(data, GOODIX_FW_DSP_LENGTH); |
69 | if (checksum) { |
70 | dev_err(dev, "DSP firmware checksum error\n" ); |
71 | return -EINVAL; |
72 | } |
73 | |
74 | fw_header = (const struct goodix_fw_header *)fw->data; |
75 | dev_info(dev, "Firmware hardware info %02x%02x%02x%02x\n" , |
76 | fw_header->hw_info[0], fw_header->hw_info[1], |
77 | fw_header->hw_info[2], fw_header->hw_info[3]); |
78 | /* pid is a 8 byte buffer containing a string, weird I know */ |
79 | memcpy(buf, fw_header->pid, 8); |
80 | buf[8] = 0; |
81 | dev_info(dev, "Firmware PID: %s VID: %02x%02x\n" , buf, |
82 | fw_header->vid[0], fw_header->vid[1]); |
83 | return 0; |
84 | } |
85 | |
86 | static int goodix_enter_upload_mode(struct i2c_client *client) |
87 | { |
88 | int tries, error; |
89 | u8 val; |
90 | |
91 | tries = 200; |
92 | do { |
93 | error = goodix_i2c_write_u8(client, |
94 | GOODIX_REG_MISCTL_SWRST, value: 0x0c); |
95 | if (error) |
96 | return error; |
97 | |
98 | error = goodix_i2c_read(client, |
99 | GOODIX_REG_MISCTL_SWRST, buf: &val, len: 1); |
100 | if (error) |
101 | return error; |
102 | |
103 | if (val == 0x0c) |
104 | break; |
105 | } while (--tries); |
106 | |
107 | if (!tries) { |
108 | dev_err(&client->dev, "Error could not hold ss51 & dsp\n" ); |
109 | return -EIO; |
110 | } |
111 | |
112 | /* DSP_CK and DSP_ALU_CK PowerOn */ |
113 | error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_DSP_CTL, value: 0x00); |
114 | if (error) |
115 | return error; |
116 | |
117 | /* Disable watchdog */ |
118 | error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_TMR0_EN, value: 0x00); |
119 | if (error) |
120 | return error; |
121 | |
122 | /* Clear cache enable */ |
123 | error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_CACHE_EN, value: 0x00); |
124 | if (error) |
125 | return error; |
126 | |
127 | /* Set boot from SRAM */ |
128 | error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_BOOTCTL, value: 0x02); |
129 | if (error) |
130 | return error; |
131 | |
132 | /* Software reboot */ |
133 | error = goodix_i2c_write_u8(client, |
134 | GOODIX_REG_MISCTL_CPU_SWRST_PULSE, value: 0x01); |
135 | if (error) |
136 | return error; |
137 | |
138 | /* Clear control flag */ |
139 | error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_BOOTCTL, value: 0x00); |
140 | if (error) |
141 | return error; |
142 | |
143 | /* Set scramble */ |
144 | error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_BOOT_OPT, value: 0x00); |
145 | if (error) |
146 | return error; |
147 | |
148 | /* Enable accessing code */ |
149 | error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_MEM_CD_EN, value: 0x01); |
150 | if (error) |
151 | return error; |
152 | |
153 | return 0; |
154 | } |
155 | |
156 | static int goodix_start_firmware(struct i2c_client *client) |
157 | { |
158 | int error; |
159 | u8 val; |
160 | |
161 | /* Init software watchdog */ |
162 | error = goodix_i2c_write_u8(client, GOODIX_REG_SW_WDT, value: 0xaa); |
163 | if (error) |
164 | return error; |
165 | |
166 | /* Release SS51 & DSP */ |
167 | error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_SWRST, value: 0x00); |
168 | if (error) |
169 | return error; |
170 | |
171 | error = goodix_i2c_read(client, GOODIX_REG_SW_WDT, buf: &val, len: 1); |
172 | if (error) |
173 | return error; |
174 | |
175 | /* The value we've written to SW_WDT should have been cleared now */ |
176 | if (val == 0xaa) { |
177 | dev_err(&client->dev, "Error SW_WDT reg not cleared on fw startup\n" ); |
178 | return -EIO; |
179 | } |
180 | |
181 | /* Re-init software watchdog */ |
182 | error = goodix_i2c_write_u8(client, GOODIX_REG_SW_WDT, value: 0xaa); |
183 | if (error) |
184 | return error; |
185 | |
186 | return 0; |
187 | } |
188 | |
189 | static int goodix_firmware_upload(struct goodix_ts_data *ts) |
190 | { |
191 | const struct firmware *fw; |
192 | char fw_name[64]; |
193 | const u8 *data; |
194 | int error; |
195 | |
196 | snprintf(buf: fw_name, size: sizeof(fw_name), fmt: "goodix/%s" , ts->firmware_name); |
197 | |
198 | error = request_firmware(fw: &fw, name: fw_name, device: &ts->client->dev); |
199 | if (error) { |
200 | dev_err(&ts->client->dev, "Firmware request error %d\n" , error); |
201 | return error; |
202 | } |
203 | |
204 | error = goodix_firmware_verify(dev: &ts->client->dev, fw); |
205 | if (error) |
206 | goto release; |
207 | |
208 | error = goodix_reset_no_int_sync(ts); |
209 | if (error) |
210 | goto release; |
211 | |
212 | error = goodix_enter_upload_mode(client: ts->client); |
213 | if (error) |
214 | goto release; |
215 | |
216 | /* Select SRAM bank 0 and upload section 1 & 2 */ |
217 | error = goodix_i2c_write_u8(client: ts->client, |
218 | GOODIX_REG_MISCTL_SRAM_BANK, value: 0x00); |
219 | if (error) |
220 | goto release; |
221 | |
222 | data = fw->data + GOODIX_FW_HEADER_LENGTH; |
223 | error = goodix_i2c_write(client: ts->client, GOODIX_FW_UPLOAD_ADDRESS, |
224 | buf: data, len: 2 * GOODIX_FW_SECTION_LENGTH); |
225 | if (error) |
226 | goto release; |
227 | |
228 | /* Select SRAM bank 1 and upload section 3 & 4 */ |
229 | error = goodix_i2c_write_u8(client: ts->client, |
230 | GOODIX_REG_MISCTL_SRAM_BANK, value: 0x01); |
231 | if (error) |
232 | goto release; |
233 | |
234 | data += 2 * GOODIX_FW_SECTION_LENGTH; |
235 | error = goodix_i2c_write(client: ts->client, GOODIX_FW_UPLOAD_ADDRESS, |
236 | buf: data, len: 2 * GOODIX_FW_SECTION_LENGTH); |
237 | if (error) |
238 | goto release; |
239 | |
240 | /* Select SRAM bank 2 and upload the DSP firmware */ |
241 | error = goodix_i2c_write_u8(client: ts->client, |
242 | GOODIX_REG_MISCTL_SRAM_BANK, value: 0x02); |
243 | if (error) |
244 | goto release; |
245 | |
246 | data += 2 * GOODIX_FW_SECTION_LENGTH; |
247 | error = goodix_i2c_write(client: ts->client, GOODIX_FW_UPLOAD_ADDRESS, |
248 | buf: data, GOODIX_FW_DSP_LENGTH); |
249 | if (error) |
250 | goto release; |
251 | |
252 | error = goodix_start_firmware(client: ts->client); |
253 | if (error) |
254 | goto release; |
255 | |
256 | error = goodix_int_sync(ts); |
257 | release: |
258 | release_firmware(fw); |
259 | return error; |
260 | } |
261 | |
262 | static int goodix_prepare_bak_ref(struct goodix_ts_data *ts) |
263 | { |
264 | u8 have_key, driver_num, sensor_num; |
265 | |
266 | if (ts->bak_ref) |
267 | return 0; /* Already done */ |
268 | |
269 | have_key = (ts->config[GOODIX_CFG_LOC_HAVE_KEY] & 0x01); |
270 | |
271 | driver_num = (ts->config[GOODIX_CFG_LOC_DRVA_NUM] & 0x1f) + |
272 | (ts->config[GOODIX_CFG_LOC_DRVB_NUM] & 0x1f); |
273 | if (have_key) |
274 | driver_num--; |
275 | |
276 | sensor_num = (ts->config[GOODIX_CFG_LOC_SENS_NUM] & 0x0f) + |
277 | ((ts->config[GOODIX_CFG_LOC_SENS_NUM] >> 4) & 0x0f); |
278 | |
279 | dev_dbg(&ts->client->dev, "Drv %d Sen %d Key %d\n" , |
280 | driver_num, sensor_num, have_key); |
281 | |
282 | ts->bak_ref_len = (driver_num * (sensor_num - 2) + 2) * 2; |
283 | |
284 | ts->bak_ref = devm_kzalloc(dev: &ts->client->dev, |
285 | size: ts->bak_ref_len, GFP_KERNEL); |
286 | if (!ts->bak_ref) |
287 | return -ENOMEM; |
288 | |
289 | /* |
290 | * The bak_ref array contains the backup of an array of (self/auto) |
291 | * calibration related values which the Android version of the driver |
292 | * stores on the filesystem so that it can be restored after reboot. |
293 | * The mainline kernel never writes directly to the filesystem like |
294 | * this, we always start will all the values which give a correction |
295 | * factor in approx. the -20 - +20 range (in 2s complement) set to 0. |
296 | * |
297 | * Note the touchscreen works fine without restoring the reference |
298 | * values after a reboot / power-cycle. |
299 | * |
300 | * The last 2 bytes are a 16 bits unsigned checksum which is expected |
301 | * to make the addition al all 16 bit unsigned values in the array add |
302 | * up to 1 (rather then the usual 0), so we must set the last byte to 1. |
303 | */ |
304 | ts->bak_ref[ts->bak_ref_len - 1] = 1; |
305 | |
306 | return 0; |
307 | } |
308 | |
309 | static int goodix_send_main_clock(struct goodix_ts_data *ts) |
310 | { |
311 | u32 main_clk = 54; /* Default main clock */ |
312 | u8 checksum = 0; |
313 | int i; |
314 | |
315 | device_property_read_u32(dev: &ts->client->dev, |
316 | propname: "goodix,main-clk" , val: &main_clk); |
317 | |
318 | for (i = 0; i < (GOODIX_MAIN_CLK_LEN - 1); i++) { |
319 | ts->main_clk[i] = main_clk; |
320 | checksum += main_clk; |
321 | } |
322 | |
323 | /* The value of all bytes combines must be 0 */ |
324 | ts->main_clk[GOODIX_MAIN_CLK_LEN - 1] = 256 - checksum; |
325 | |
326 | return goodix_i2c_write(client: ts->client, GOODIX_REG_MAIN_CLK, |
327 | buf: ts->main_clk, GOODIX_MAIN_CLK_LEN); |
328 | } |
329 | |
330 | int goodix_firmware_check(struct goodix_ts_data *ts) |
331 | { |
332 | device_property_read_string(dev: &ts->client->dev, |
333 | propname: "firmware-name" , val: &ts->firmware_name); |
334 | if (!ts->firmware_name) |
335 | return 0; |
336 | |
337 | if (ts->irq_pin_access_method == IRQ_PIN_ACCESS_NONE) { |
338 | dev_err(&ts->client->dev, "Error no IRQ-pin access method, cannot upload fw.\n" ); |
339 | return -EINVAL; |
340 | } |
341 | |
342 | dev_info(&ts->client->dev, "Touchscreen controller needs fw-upload\n" ); |
343 | ts->load_cfg_from_disk = true; |
344 | |
345 | return goodix_firmware_upload(ts); |
346 | } |
347 | |
348 | bool goodix_handle_fw_request(struct goodix_ts_data *ts) |
349 | { |
350 | int error; |
351 | u8 val; |
352 | |
353 | error = goodix_i2c_read(client: ts->client, GOODIX_REG_REQUEST, buf: &val, len: 1); |
354 | if (error) |
355 | return false; |
356 | |
357 | switch (val) { |
358 | case GOODIX_RQST_RESPONDED: |
359 | /* |
360 | * If we read back our own last ack the IRQ was not for |
361 | * a request. |
362 | */ |
363 | return false; |
364 | case GOODIX_RQST_CONFIG: |
365 | error = goodix_send_cfg(ts, cfg: ts->config, len: ts->chip->config_len); |
366 | if (error) |
367 | return false; |
368 | |
369 | break; |
370 | case GOODIX_RQST_BAK_REF: |
371 | error = goodix_prepare_bak_ref(ts); |
372 | if (error) |
373 | return false; |
374 | |
375 | error = goodix_i2c_write(client: ts->client, GOODIX_REG_BAK_REF, |
376 | buf: ts->bak_ref, len: ts->bak_ref_len); |
377 | if (error) |
378 | return false; |
379 | |
380 | break; |
381 | case GOODIX_RQST_RESET: |
382 | error = goodix_firmware_upload(ts); |
383 | if (error) |
384 | return false; |
385 | |
386 | break; |
387 | case GOODIX_RQST_MAIN_CLOCK: |
388 | error = goodix_send_main_clock(ts); |
389 | if (error) |
390 | return false; |
391 | |
392 | break; |
393 | case GOODIX_RQST_UNKNOWN: |
394 | case GOODIX_RQST_IDLE: |
395 | break; |
396 | default: |
397 | dev_err_ratelimited(&ts->client->dev, "Unknown Request: 0x%02x\n" , val); |
398 | } |
399 | |
400 | /* Ack the request */ |
401 | goodix_i2c_write_u8(client: ts->client, |
402 | GOODIX_REG_REQUEST, GOODIX_RQST_RESPONDED); |
403 | return true; |
404 | } |
405 | |
406 | void goodix_save_bak_ref(struct goodix_ts_data *ts) |
407 | { |
408 | int error; |
409 | u8 val; |
410 | |
411 | if (!ts->firmware_name) |
412 | return; |
413 | |
414 | error = goodix_i2c_read(client: ts->client, GOODIX_REG_STATUS, buf: &val, len: 1); |
415 | if (error) |
416 | return; |
417 | |
418 | if (!(val & 0x80)) |
419 | return; |
420 | |
421 | error = goodix_i2c_read(client: ts->client, GOODIX_REG_BAK_REF, |
422 | buf: ts->bak_ref, len: ts->bak_ref_len); |
423 | if (error) { |
424 | memset(ts->bak_ref, 0, ts->bak_ref_len); |
425 | ts->bak_ref[ts->bak_ref_len - 1] = 1; |
426 | } |
427 | } |
428 | |