1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2007-2016, Synaptics Incorporated |
4 | * Copyright (C) 2016 Zodiac Inflight Innovations |
5 | */ |
6 | |
7 | #include <linux/kernel.h> |
8 | #include <linux/rmi.h> |
9 | #include <linux/firmware.h> |
10 | #include <asm/unaligned.h> |
11 | #include <linux/bitops.h> |
12 | |
13 | #include "rmi_driver.h" |
14 | #include "rmi_f34.h" |
15 | |
16 | static int rmi_f34_write_bootloader_id(struct f34_data *f34) |
17 | { |
18 | struct rmi_function *fn = f34->fn; |
19 | struct rmi_device *rmi_dev = fn->rmi_dev; |
20 | u8 bootloader_id[F34_BOOTLOADER_ID_LEN]; |
21 | int ret; |
22 | |
23 | ret = rmi_read_block(d: rmi_dev, addr: fn->fd.query_base_addr, |
24 | buf: bootloader_id, len: sizeof(bootloader_id)); |
25 | if (ret) { |
26 | dev_err(&fn->dev, "%s: Reading bootloader ID failed: %d\n" , |
27 | __func__, ret); |
28 | return ret; |
29 | } |
30 | |
31 | rmi_dbg(RMI_DEBUG_FN, dev: &fn->dev, fmt: "%s: writing bootloader id '%c%c'\n" , |
32 | __func__, bootloader_id[0], bootloader_id[1]); |
33 | |
34 | ret = rmi_write_block(d: rmi_dev, |
35 | addr: fn->fd.data_base_addr + F34_BLOCK_DATA_OFFSET, |
36 | buf: bootloader_id, len: sizeof(bootloader_id)); |
37 | if (ret) { |
38 | dev_err(&fn->dev, "Failed to write bootloader ID: %d\n" , ret); |
39 | return ret; |
40 | } |
41 | |
42 | return 0; |
43 | } |
44 | |
45 | static int rmi_f34_command(struct f34_data *f34, u8 command, |
46 | unsigned int timeout, bool write_bl_id) |
47 | { |
48 | struct rmi_function *fn = f34->fn; |
49 | struct rmi_device *rmi_dev = fn->rmi_dev; |
50 | int ret; |
51 | |
52 | if (write_bl_id) { |
53 | ret = rmi_f34_write_bootloader_id(f34); |
54 | if (ret) |
55 | return ret; |
56 | } |
57 | |
58 | init_completion(x: &f34->v5.cmd_done); |
59 | |
60 | ret = rmi_read(d: rmi_dev, addr: f34->v5.ctrl_address, buf: &f34->v5.status); |
61 | if (ret) { |
62 | dev_err(&f34->fn->dev, |
63 | "%s: Failed to read cmd register: %d (command %#02x)\n" , |
64 | __func__, ret, command); |
65 | return ret; |
66 | } |
67 | |
68 | f34->v5.status |= command & 0x0f; |
69 | |
70 | ret = rmi_write(d: rmi_dev, addr: f34->v5.ctrl_address, data: f34->v5.status); |
71 | if (ret < 0) { |
72 | dev_err(&f34->fn->dev, |
73 | "Failed to write F34 command %#02x: %d\n" , |
74 | command, ret); |
75 | return ret; |
76 | } |
77 | |
78 | if (!wait_for_completion_timeout(x: &f34->v5.cmd_done, |
79 | timeout: msecs_to_jiffies(m: timeout))) { |
80 | |
81 | ret = rmi_read(d: rmi_dev, addr: f34->v5.ctrl_address, buf: &f34->v5.status); |
82 | if (ret) { |
83 | dev_err(&f34->fn->dev, |
84 | "%s: cmd %#02x timed out: %d\n" , |
85 | __func__, command, ret); |
86 | return ret; |
87 | } |
88 | |
89 | if (f34->v5.status & 0x7f) { |
90 | dev_err(&f34->fn->dev, |
91 | "%s: cmd %#02x timed out, status: %#02x\n" , |
92 | __func__, command, f34->v5.status); |
93 | return -ETIMEDOUT; |
94 | } |
95 | } |
96 | |
97 | return 0; |
98 | } |
99 | |
100 | static irqreturn_t rmi_f34_attention(int irq, void *ctx) |
101 | { |
102 | struct rmi_function *fn = ctx; |
103 | struct f34_data *f34 = dev_get_drvdata(dev: &fn->dev); |
104 | int ret; |
105 | u8 status; |
106 | |
107 | if (f34->bl_version == 5) { |
108 | ret = rmi_read(d: f34->fn->rmi_dev, addr: f34->v5.ctrl_address, |
109 | buf: &status); |
110 | rmi_dbg(RMI_DEBUG_FN, dev: &fn->dev, fmt: "%s: status: %#02x, ret: %d\n" , |
111 | __func__, status, ret); |
112 | |
113 | if (!ret && !(status & 0x7f)) |
114 | complete(&f34->v5.cmd_done); |
115 | } else { |
116 | ret = rmi_read_block(d: f34->fn->rmi_dev, |
117 | addr: f34->fn->fd.data_base_addr + |
118 | V7_COMMAND_OFFSET, |
119 | buf: &status, len: sizeof(status)); |
120 | rmi_dbg(RMI_DEBUG_FN, dev: &f34->fn->dev, fmt: "%s: cmd: %#02x, ret: %d\n" , |
121 | __func__, status, ret); |
122 | |
123 | if (!ret && status == CMD_V7_IDLE) |
124 | complete(&f34->v7.cmd_done); |
125 | } |
126 | |
127 | return IRQ_HANDLED; |
128 | } |
129 | |
130 | static int rmi_f34_write_blocks(struct f34_data *f34, const void *data, |
131 | int block_count, u8 command) |
132 | { |
133 | struct rmi_function *fn = f34->fn; |
134 | struct rmi_device *rmi_dev = fn->rmi_dev; |
135 | u16 address = fn->fd.data_base_addr + F34_BLOCK_DATA_OFFSET; |
136 | u8 start_address[] = { 0, 0 }; |
137 | int i; |
138 | int ret; |
139 | |
140 | ret = rmi_write_block(d: rmi_dev, addr: fn->fd.data_base_addr, |
141 | buf: start_address, len: sizeof(start_address)); |
142 | if (ret) { |
143 | dev_err(&fn->dev, "Failed to write initial zeros: %d\n" , ret); |
144 | return ret; |
145 | } |
146 | |
147 | for (i = 0; i < block_count; i++) { |
148 | ret = rmi_write_block(d: rmi_dev, addr: address, |
149 | buf: data, len: f34->v5.block_size); |
150 | if (ret) { |
151 | dev_err(&fn->dev, |
152 | "failed to write block #%d: %d\n" , i, ret); |
153 | return ret; |
154 | } |
155 | |
156 | ret = rmi_f34_command(f34, command, F34_IDLE_WAIT_MS, write_bl_id: false); |
157 | if (ret) { |
158 | dev_err(&fn->dev, |
159 | "Failed to write command for block #%d: %d\n" , |
160 | i, ret); |
161 | return ret; |
162 | } |
163 | |
164 | rmi_dbg(RMI_DEBUG_FN, dev: &fn->dev, fmt: "wrote block %d of %d\n" , |
165 | i + 1, block_count); |
166 | |
167 | data += f34->v5.block_size; |
168 | f34->update_progress += f34->v5.block_size; |
169 | f34->update_status = (f34->update_progress * 100) / |
170 | f34->update_size; |
171 | } |
172 | |
173 | return 0; |
174 | } |
175 | |
176 | static int rmi_f34_write_firmware(struct f34_data *f34, const void *data) |
177 | { |
178 | return rmi_f34_write_blocks(f34, data, block_count: f34->v5.fw_blocks, |
179 | F34_WRITE_FW_BLOCK); |
180 | } |
181 | |
182 | static int rmi_f34_write_config(struct f34_data *f34, const void *data) |
183 | { |
184 | return rmi_f34_write_blocks(f34, data, block_count: f34->v5.config_blocks, |
185 | F34_WRITE_CONFIG_BLOCK); |
186 | } |
187 | |
188 | static int rmi_f34_enable_flash(struct f34_data *f34) |
189 | { |
190 | return rmi_f34_command(f34, F34_ENABLE_FLASH_PROG, |
191 | F34_ENABLE_WAIT_MS, write_bl_id: true); |
192 | } |
193 | |
194 | static int rmi_f34_flash_firmware(struct f34_data *f34, |
195 | const struct rmi_f34_firmware *syn_fw) |
196 | { |
197 | struct rmi_function *fn = f34->fn; |
198 | u32 image_size = le32_to_cpu(syn_fw->image_size); |
199 | u32 config_size = le32_to_cpu(syn_fw->config_size); |
200 | int ret; |
201 | |
202 | f34->update_progress = 0; |
203 | f34->update_size = image_size + config_size; |
204 | |
205 | if (image_size) { |
206 | dev_info(&fn->dev, "Erasing firmware...\n" ); |
207 | ret = rmi_f34_command(f34, F34_ERASE_ALL, |
208 | F34_ERASE_WAIT_MS, write_bl_id: true); |
209 | if (ret) |
210 | return ret; |
211 | |
212 | dev_info(&fn->dev, "Writing firmware (%d bytes)...\n" , |
213 | image_size); |
214 | ret = rmi_f34_write_firmware(f34, data: syn_fw->data); |
215 | if (ret) |
216 | return ret; |
217 | } |
218 | |
219 | if (config_size) { |
220 | /* |
221 | * We only need to erase config if we haven't updated |
222 | * firmware. |
223 | */ |
224 | if (!image_size) { |
225 | dev_info(&fn->dev, "Erasing config...\n" ); |
226 | ret = rmi_f34_command(f34, F34_ERASE_CONFIG, |
227 | F34_ERASE_WAIT_MS, write_bl_id: true); |
228 | if (ret) |
229 | return ret; |
230 | } |
231 | |
232 | dev_info(&fn->dev, "Writing config (%d bytes)...\n" , |
233 | config_size); |
234 | ret = rmi_f34_write_config(f34, data: &syn_fw->data[image_size]); |
235 | if (ret) |
236 | return ret; |
237 | } |
238 | |
239 | return 0; |
240 | } |
241 | |
242 | static int rmi_f34_update_firmware(struct f34_data *f34, |
243 | const struct firmware *fw) |
244 | { |
245 | const struct rmi_f34_firmware *syn_fw = |
246 | (const struct rmi_f34_firmware *)fw->data; |
247 | u32 image_size = le32_to_cpu(syn_fw->image_size); |
248 | u32 config_size = le32_to_cpu(syn_fw->config_size); |
249 | int ret; |
250 | |
251 | BUILD_BUG_ON(offsetof(struct rmi_f34_firmware, data) != |
252 | F34_FW_IMAGE_OFFSET); |
253 | |
254 | rmi_dbg(RMI_DEBUG_FN, dev: &f34->fn->dev, |
255 | fmt: "FW size:%zd, checksum:%08x, image_size:%d, config_size:%d\n" , |
256 | fw->size, |
257 | le32_to_cpu(syn_fw->checksum), |
258 | image_size, config_size); |
259 | |
260 | rmi_dbg(RMI_DEBUG_FN, dev: &f34->fn->dev, |
261 | fmt: "FW bootloader_id:%02x, product_id:%.*s, info: %02x%02x\n" , |
262 | syn_fw->bootloader_version, |
263 | (int)sizeof(syn_fw->product_id), syn_fw->product_id, |
264 | syn_fw->product_info[0], syn_fw->product_info[1]); |
265 | |
266 | if (image_size && image_size != f34->v5.fw_blocks * f34->v5.block_size) { |
267 | dev_err(&f34->fn->dev, |
268 | "Bad firmware image: fw size %d, expected %d\n" , |
269 | image_size, f34->v5.fw_blocks * f34->v5.block_size); |
270 | ret = -EILSEQ; |
271 | goto out; |
272 | } |
273 | |
274 | if (config_size && |
275 | config_size != f34->v5.config_blocks * f34->v5.block_size) { |
276 | dev_err(&f34->fn->dev, |
277 | "Bad firmware image: config size %d, expected %d\n" , |
278 | config_size, |
279 | f34->v5.config_blocks * f34->v5.block_size); |
280 | ret = -EILSEQ; |
281 | goto out; |
282 | } |
283 | |
284 | if (image_size && !config_size) { |
285 | dev_err(&f34->fn->dev, "Bad firmware image: no config data\n" ); |
286 | ret = -EILSEQ; |
287 | goto out; |
288 | } |
289 | |
290 | dev_info(&f34->fn->dev, "Firmware image OK\n" ); |
291 | mutex_lock(&f34->v5.flash_mutex); |
292 | |
293 | ret = rmi_f34_flash_firmware(f34, syn_fw); |
294 | |
295 | mutex_unlock(lock: &f34->v5.flash_mutex); |
296 | |
297 | out: |
298 | return ret; |
299 | } |
300 | |
301 | static int rmi_f34_status(struct rmi_function *fn) |
302 | { |
303 | struct f34_data *f34 = dev_get_drvdata(dev: &fn->dev); |
304 | |
305 | /* |
306 | * The status is the percentage complete, or once complete, |
307 | * zero for success or a negative return code. |
308 | */ |
309 | return f34->update_status; |
310 | } |
311 | |
312 | static ssize_t rmi_driver_bootloader_id_show(struct device *dev, |
313 | struct device_attribute *dattr, |
314 | char *buf) |
315 | { |
316 | struct rmi_driver_data *data = dev_get_drvdata(dev); |
317 | struct rmi_function *fn = data->f34_container; |
318 | struct f34_data *f34; |
319 | |
320 | if (fn) { |
321 | f34 = dev_get_drvdata(dev: &fn->dev); |
322 | |
323 | if (f34->bl_version == 5) |
324 | return sysfs_emit(buf, fmt: "%c%c\n" , |
325 | f34->bootloader_id[0], |
326 | f34->bootloader_id[1]); |
327 | else |
328 | return sysfs_emit(buf, fmt: "V%d.%d\n" , |
329 | f34->bootloader_id[1], |
330 | f34->bootloader_id[0]); |
331 | } |
332 | |
333 | return 0; |
334 | } |
335 | |
336 | static DEVICE_ATTR(bootloader_id, 0444, rmi_driver_bootloader_id_show, NULL); |
337 | |
338 | static ssize_t rmi_driver_configuration_id_show(struct device *dev, |
339 | struct device_attribute *dattr, |
340 | char *buf) |
341 | { |
342 | struct rmi_driver_data *data = dev_get_drvdata(dev); |
343 | struct rmi_function *fn = data->f34_container; |
344 | struct f34_data *f34; |
345 | |
346 | if (fn) { |
347 | f34 = dev_get_drvdata(dev: &fn->dev); |
348 | |
349 | return sysfs_emit(buf, fmt: "%s\n" , f34->configuration_id); |
350 | } |
351 | |
352 | return 0; |
353 | } |
354 | |
355 | static DEVICE_ATTR(configuration_id, 0444, |
356 | rmi_driver_configuration_id_show, NULL); |
357 | |
358 | static int rmi_firmware_update(struct rmi_driver_data *data, |
359 | const struct firmware *fw) |
360 | { |
361 | struct rmi_device *rmi_dev = data->rmi_dev; |
362 | struct device *dev = &rmi_dev->dev; |
363 | struct f34_data *f34; |
364 | int ret; |
365 | |
366 | if (!data->f34_container) { |
367 | dev_warn(dev, "%s: No F34 present!\n" , __func__); |
368 | return -EINVAL; |
369 | } |
370 | |
371 | f34 = dev_get_drvdata(dev: &data->f34_container->dev); |
372 | |
373 | if (f34->bl_version >= 7) { |
374 | if (data->pdt_props & HAS_BSR) { |
375 | dev_err(dev, "%s: LTS not supported\n" , __func__); |
376 | return -ENODEV; |
377 | } |
378 | } else if (f34->bl_version != 5) { |
379 | dev_warn(dev, "F34 V%d not supported!\n" , |
380 | data->f34_container->fd.function_version); |
381 | return -ENODEV; |
382 | } |
383 | |
384 | /* Enter flash mode */ |
385 | if (f34->bl_version >= 7) |
386 | ret = rmi_f34v7_start_reflash(f34, fw); |
387 | else |
388 | ret = rmi_f34_enable_flash(f34); |
389 | if (ret) |
390 | return ret; |
391 | |
392 | rmi_disable_irq(rmi_dev, enable_wake: false); |
393 | |
394 | /* Tear down functions and re-probe */ |
395 | rmi_free_function_list(rmi_dev); |
396 | |
397 | ret = rmi_probe_interrupts(data); |
398 | if (ret) |
399 | return ret; |
400 | |
401 | ret = rmi_init_functions(data); |
402 | if (ret) |
403 | return ret; |
404 | |
405 | if (!data->bootloader_mode || !data->f34_container) { |
406 | dev_warn(dev, "%s: No F34 present or not in bootloader!\n" , |
407 | __func__); |
408 | return -EINVAL; |
409 | } |
410 | |
411 | rmi_enable_irq(rmi_dev, clear_wake: false); |
412 | |
413 | f34 = dev_get_drvdata(dev: &data->f34_container->dev); |
414 | |
415 | /* Perform firmware update */ |
416 | if (f34->bl_version >= 7) |
417 | ret = rmi_f34v7_do_reflash(f34, fw); |
418 | else |
419 | ret = rmi_f34_update_firmware(f34, fw); |
420 | |
421 | if (ret) { |
422 | f34->update_status = ret; |
423 | dev_err(&f34->fn->dev, |
424 | "Firmware update failed, status: %d\n" , ret); |
425 | } else { |
426 | dev_info(&f34->fn->dev, "Firmware update complete\n" ); |
427 | } |
428 | |
429 | rmi_disable_irq(rmi_dev, enable_wake: false); |
430 | |
431 | /* Re-probe */ |
432 | rmi_dbg(RMI_DEBUG_FN, dev, fmt: "Re-probing device\n" ); |
433 | rmi_free_function_list(rmi_dev); |
434 | |
435 | ret = rmi_scan_pdt(rmi_dev, NULL, callback: rmi_initial_reset); |
436 | if (ret < 0) |
437 | dev_warn(dev, "RMI reset failed!\n" ); |
438 | |
439 | ret = rmi_probe_interrupts(data); |
440 | if (ret) |
441 | return ret; |
442 | |
443 | ret = rmi_init_functions(data); |
444 | if (ret) |
445 | return ret; |
446 | |
447 | rmi_enable_irq(rmi_dev, clear_wake: false); |
448 | |
449 | if (data->f01_container->dev.driver) |
450 | /* Driver already bound, so enable ATTN now. */ |
451 | return rmi_enable_sensor(rmi_dev); |
452 | |
453 | rmi_dbg(RMI_DEBUG_FN, dev, fmt: "%s complete\n" , __func__); |
454 | |
455 | return ret; |
456 | } |
457 | |
458 | static ssize_t rmi_driver_update_fw_store(struct device *dev, |
459 | struct device_attribute *dattr, |
460 | const char *buf, size_t count) |
461 | { |
462 | struct rmi_driver_data *data = dev_get_drvdata(dev); |
463 | char fw_name[NAME_MAX]; |
464 | const struct firmware *fw; |
465 | size_t copy_count = count; |
466 | int ret; |
467 | |
468 | if (count == 0 || count >= NAME_MAX) |
469 | return -EINVAL; |
470 | |
471 | if (buf[count - 1] == '\0' || buf[count - 1] == '\n') |
472 | copy_count -= 1; |
473 | |
474 | strncpy(p: fw_name, q: buf, size: copy_count); |
475 | fw_name[copy_count] = '\0'; |
476 | |
477 | ret = request_firmware(fw: &fw, name: fw_name, device: dev); |
478 | if (ret) |
479 | return ret; |
480 | |
481 | dev_info(dev, "Flashing %s\n" , fw_name); |
482 | |
483 | ret = rmi_firmware_update(data, fw); |
484 | |
485 | release_firmware(fw); |
486 | |
487 | return ret ?: count; |
488 | } |
489 | |
490 | static DEVICE_ATTR(update_fw, 0200, NULL, rmi_driver_update_fw_store); |
491 | |
492 | static ssize_t rmi_driver_update_fw_status_show(struct device *dev, |
493 | struct device_attribute *dattr, |
494 | char *buf) |
495 | { |
496 | struct rmi_driver_data *data = dev_get_drvdata(dev); |
497 | int update_status = 0; |
498 | |
499 | if (data->f34_container) |
500 | update_status = rmi_f34_status(fn: data->f34_container); |
501 | |
502 | return sysfs_emit(buf, fmt: "%d\n" , update_status); |
503 | } |
504 | |
505 | static DEVICE_ATTR(update_fw_status, 0444, |
506 | rmi_driver_update_fw_status_show, NULL); |
507 | |
508 | static struct attribute *rmi_firmware_attrs[] = { |
509 | &dev_attr_bootloader_id.attr, |
510 | &dev_attr_configuration_id.attr, |
511 | &dev_attr_update_fw.attr, |
512 | &dev_attr_update_fw_status.attr, |
513 | NULL |
514 | }; |
515 | |
516 | static const struct attribute_group rmi_firmware_attr_group = { |
517 | .attrs = rmi_firmware_attrs, |
518 | }; |
519 | |
520 | static int rmi_f34_probe(struct rmi_function *fn) |
521 | { |
522 | struct f34_data *f34; |
523 | unsigned char f34_queries[9]; |
524 | bool has_config_id; |
525 | u8 version = fn->fd.function_version; |
526 | int ret; |
527 | |
528 | f34 = devm_kzalloc(dev: &fn->dev, size: sizeof(struct f34_data), GFP_KERNEL); |
529 | if (!f34) |
530 | return -ENOMEM; |
531 | |
532 | f34->fn = fn; |
533 | dev_set_drvdata(dev: &fn->dev, data: f34); |
534 | |
535 | /* v5 code only supported version 0, try V7 probe */ |
536 | if (version > 0) |
537 | return rmi_f34v7_probe(f34); |
538 | |
539 | f34->bl_version = 5; |
540 | |
541 | ret = rmi_read_block(d: fn->rmi_dev, addr: fn->fd.query_base_addr, |
542 | buf: f34_queries, len: sizeof(f34_queries)); |
543 | if (ret) { |
544 | dev_err(&fn->dev, "%s: Failed to query properties\n" , |
545 | __func__); |
546 | return ret; |
547 | } |
548 | |
549 | snprintf(buf: f34->bootloader_id, size: sizeof(f34->bootloader_id), |
550 | fmt: "%c%c" , f34_queries[0], f34_queries[1]); |
551 | |
552 | mutex_init(&f34->v5.flash_mutex); |
553 | init_completion(x: &f34->v5.cmd_done); |
554 | |
555 | f34->v5.block_size = get_unaligned_le16(p: &f34_queries[3]); |
556 | f34->v5.fw_blocks = get_unaligned_le16(p: &f34_queries[5]); |
557 | f34->v5.config_blocks = get_unaligned_le16(p: &f34_queries[7]); |
558 | f34->v5.ctrl_address = fn->fd.data_base_addr + F34_BLOCK_DATA_OFFSET + |
559 | f34->v5.block_size; |
560 | has_config_id = f34_queries[2] & (1 << 2); |
561 | |
562 | rmi_dbg(RMI_DEBUG_FN, dev: &fn->dev, fmt: "Bootloader ID: %s\n" , |
563 | f34->bootloader_id); |
564 | rmi_dbg(RMI_DEBUG_FN, dev: &fn->dev, fmt: "Block size: %d\n" , |
565 | f34->v5.block_size); |
566 | rmi_dbg(RMI_DEBUG_FN, dev: &fn->dev, fmt: "FW blocks: %d\n" , |
567 | f34->v5.fw_blocks); |
568 | rmi_dbg(RMI_DEBUG_FN, dev: &fn->dev, fmt: "CFG blocks: %d\n" , |
569 | f34->v5.config_blocks); |
570 | |
571 | if (has_config_id) { |
572 | ret = rmi_read_block(d: fn->rmi_dev, addr: fn->fd.control_base_addr, |
573 | buf: f34_queries, len: sizeof(f34_queries)); |
574 | if (ret) { |
575 | dev_err(&fn->dev, "Failed to read F34 config ID\n" ); |
576 | return ret; |
577 | } |
578 | |
579 | snprintf(buf: f34->configuration_id, size: sizeof(f34->configuration_id), |
580 | fmt: "%02x%02x%02x%02x" , |
581 | f34_queries[0], f34_queries[1], |
582 | f34_queries[2], f34_queries[3]); |
583 | |
584 | rmi_dbg(RMI_DEBUG_FN, dev: &fn->dev, fmt: "Configuration ID: %s\n" , |
585 | f34->configuration_id); |
586 | } |
587 | |
588 | return 0; |
589 | } |
590 | |
591 | int rmi_f34_create_sysfs(struct rmi_device *rmi_dev) |
592 | { |
593 | return sysfs_create_group(kobj: &rmi_dev->dev.kobj, grp: &rmi_firmware_attr_group); |
594 | } |
595 | |
596 | void rmi_f34_remove_sysfs(struct rmi_device *rmi_dev) |
597 | { |
598 | sysfs_remove_group(kobj: &rmi_dev->dev.kobj, grp: &rmi_firmware_attr_group); |
599 | } |
600 | |
601 | struct rmi_function_handler rmi_f34_handler = { |
602 | .driver = { |
603 | .name = "rmi4_f34" , |
604 | }, |
605 | .func = 0x34, |
606 | .probe = rmi_f34_probe, |
607 | .attention = rmi_f34_attention, |
608 | }; |
609 | |