exynos_soc.c revision 1.20 1 /* $NetBSD: exynos_soc.c,v 1.20 2014/09/05 08:01:05 skrll Exp $ */
2
3 /*-
4 * Copyright (c) 2014 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Reinoud Zandijk.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "opt_exynos.h"
33
34 #define _ARM32_BUS_DMA_PRIVATE
35
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(1, "$NetBSD: exynos_soc.c,v 1.20 2014/09/05 08:01:05 skrll Exp $");
38
39 #include <sys/param.h>
40 #include <sys/bus.h>
41 #include <sys/cpu.h>
42 #include <sys/device.h>
43
44 #include <prop/proplib.h>
45
46 #include <net/if.h>
47 #include <net/if_ether.h>
48
49 #include <arm/locore.h>
50
51 #include <arm/mainbus/mainbus.h>
52 #include <arm/cortex/mpcore_var.h>
53
54 #include <arm/samsung/exynos_reg.h>
55 #include <arm/samsung/exynos_var.h>
56 #include <arm/samsung/mct_reg.h>
57 #include <arm/samsung/smc.h>
58
59 #include <arm/cortex/pl310_var.h>
60 #include <arm/cortex/pl310_reg.h>
61
62 /* XXXNH */
63 #include <evbarm/odroid/platform.h>
64
65 bus_space_handle_t exynos_core_bsh;
66 bus_space_handle_t exynos_audiocore_bsh;
67
68 /* these variables are retrieved in start.S and stored in .data */
69 uint32_t exynos_soc_id = 0;
70 uint32_t exynos_pop_id = 0;
71
72
73 /* cpu frequencies */
74 struct cpu_freq {
75 uint64_t freq;
76 int P;
77 int M;
78 int S;
79 };
80
81
82 #ifdef EXYNOS4
83 const struct cpu_freq cpu_freq_settings_exynos4[] = {
84 { 200, 3, 100, 2},
85 { 300, 4, 200, 2},
86 { 400, 3, 100, 1},
87 { 500, 3, 125, 1},
88 { 600, 4, 200, 1},
89 { 700, 3, 175, 1},
90 { 800, 3, 100, 0},
91 { 900, 4, 150, 0},
92 {1000, 3, 125, 0},
93 {1100, 6, 275, 0},
94 {1200, 4, 200, 0},
95 {1300, 6, 325, 0},
96 {1400, 3, 175, 0},
97 {1600, 3, 200, 0},
98 };
99 #endif
100
101
102 #ifdef EXYNOS5
103 const struct cpu_freq cpu_freq_settings_exynos5[] = {
104 { 200, 3, 100, 2},
105 { 333, 4, 222, 2},
106 { 400, 3, 100, 1},
107 { 533, 12, 533, 1},
108 { 600, 4, 200, 1},
109 { 667, 7, 389, 1},
110 { 800, 3, 100, 0},
111 {1000, 3, 125, 0},
112 {1066, 12, 533, 0},
113 {1200, 3, 150, 0},
114 {1400, 3, 175, 0},
115 {1600, 3, 200, 0},
116 };
117 #endif
118
119 static struct cpu_freq const *cpu_freq_settings = NULL;
120 static int ncpu_freq_settings = 0;
121
122 static int cpu_freq_target = 0;
123 #define NFRQS 15
124 static char sysctl_cpu_freqs_txt[NFRQS*5];
125
126 static int sysctl_cpufreq_target(SYSCTLFN_ARGS);
127 static int sysctl_cpufreq_current(SYSCTLFN_ARGS);
128
129
130 /*
131 * the early serial console
132 */
133 #ifdef EXYNOS_CONSOLE_EARLY
134
135 #include "opt_sscom.h"
136 #include <arm/samsung/sscom_reg.h>
137 #include <arm/samsung/sscom_var.h>
138 #include <dev/cons.h>
139
140 static volatile uint8_t *uart_base;
141
142 #define CON_REG(a) (*((volatile uint32_t *)(uart_base + (a))))
143
144 static int
145 exynos_cngetc(dev_t dv)
146 {
147 if ((CON_REG(SSCOM_UTRSTAT) & UTRSTAT_RXREADY) == 0)
148 return -1;
149
150 return CON_REG(SSCOM_URXH);
151 }
152
153 static void
154 exynos_cnputc(dev_t dv, int c)
155 {
156 int timo = 150000;
157
158 while ((CON_REG(SSCOM_UFSTAT) & UFSTAT_TXFULL) && --timo > 0);
159
160 CON_REG(SSCOM_UTXH) = c & 0xff;
161 }
162
163 static struct consdev exynos_earlycons = {
164 .cn_putc = exynos_cnputc,
165 .cn_getc = exynos_cngetc,
166 .cn_pollc = nullcnpollc,
167 };
168 #endif /* EXYNOS_CONSOLE_EARLY */
169
170
171 #ifdef ARM_TRUSTZONE_FIRMWARE
172 int
173 exynos_do_idle(void)
174 {
175 exynos_smc(SMC_CMD_SLEEP, 0, 0, 0);
176
177 return 0;
178 }
179
180
181 int
182 exynos_set_cpu_boot_addr(int cpu, vaddr_t boot_addr)
183 {
184 /* XXX we need to map in iRAM space for this XXX */
185 return 0;
186 }
187
188
189 int
190 exynos_cpu_boot(int cpu)
191 {
192 exynos_smc(SMC_CMD_CPU1BOOT, cpu, 0, 0);
193
194 return 0;
195 }
196
197
198 /*
199 * The latency values used below are `magic' and probably chosen empirically.
200 * For the 4210 variant the data latency is lower, a 0x110. This is currently
201 * not enforced.
202 *
203 * The prefetch values are also different for the revision 0 of the
204 * Exynos4412, but why?
205 */
206
207 int
208 exynos_l2cc_init(void)
209 {
210 const uint32_t tag_latency = 0x110;
211 const uint32_t data_latency = IS_EXYNOS4410_P() ? 0x110 : 0x120;
212 const uint32_t prefetch4412 = /* 0111 0001 0000 0000 0000 0000 0000 0111 */
213 PREFETCHCTL_DBLLINEF_EN |
214 PREFETCHCTL_INSTRPREF_EN |
215 PREFETCHCTL_DATAPREF_EN |
216 PREFETCHCTL_PREF_DROP_EN |
217 PREFETCHCTL_PREFETCH_OFFSET_7;
218 const uint32_t prefetch4412_r0 = /* 0011 0000 0000 0000 0000 0000 0000 0111 */
219 PREFETCHCTL_INSTRPREF_EN |
220 PREFETCHCTL_DATAPREF_EN |
221 PREFETCHCTL_PREFETCH_OFFSET_7;
222 const uint32_t aux_val = /* 0111 1100 0100 0111 0000 0000 0000 0001 */
223 AUXCTL_EARLY_BRESP_EN |
224 AUXCTL_I_PREFETCH |
225 AUXCTL_D_PREFETCH |
226 AUXCTL_NS_INT_ACC_CTL |
227 AUXCTL_NS_INT_LOCK_EN |
228 AUXCTL_SHARED_ATT_OVR |
229 AUXCTL_WAY_SIZE_RSVD7 << 16 | /* why rsvd7 ??? */
230 AUXCTL_FULL_LINE_WR0;
231 const uint32_t aux_keepmask = /* 1100 0010 0000 0000 1111 1111 1111 1111 */
232 AUXCTL_RSVD31 |
233 AUXCTL_EARLY_BRESP_EN |
234 AUXCTL_CACHE_REPL_RR |
235
236 AUXCTL_SH_ATTR_INV_ENA|
237 AUXCTL_EXCL_CACHE_CFG |
238 AUXCTL_ST_BUF_DEV_LIM_EN |
239 AUXCTL_HIPRO_SO_DEV_EN |
240 AUXCTL_FULL_LINE_WR0 |
241 0xffff;
242 uint32_t prefetch;
243
244 /* check the bitmaps are the same as the linux implementation uses */
245 KASSERT(prefetch4412 == 0x71000007);
246 KASSERT(prefetch4412_r0 == 0x30000007);
247 KASSERT(aux_val == 0x7C470001);
248 KASSERT(aux_keepmask == 0xC200FFFF);
249
250 if (IS_EXYNOS4412_R0_P())
251 prefetch = prefetch4412_r0;
252 else
253 prefetch = prefetch4412; /* newer than >= r1_0 */
254 ;
255
256 exynos_smc(SMC_CMD_L2X0SETUP1, tag_latency, data_latency, prefetch);
257 exynos_smc(SMC_CMD_L2X0SETUP2,
258 POWERCTL_DYNCLKGATE | POWERCTL_STANDBY,
259 aux_val, aux_keepmask);
260 exynos_smc(SMC_CMD_L2X0INVALL, 0, 0, 0);
261 exynos_smc(SMC_CMD_L2X0CTRL, 1, 0, 0);
262
263 return 0;
264 }
265 #endif /* ARM_TRUSTZONE_FIRMWARE */
266
267
268 void
269 exynos_sysctl_cpufreq_init(void)
270 {
271 const struct sysctlnode *node, *cpunode, *freqnode;
272 char *cpos;
273 int i, val;
274 int error;
275
276 memset(sysctl_cpu_freqs_txt, (int) ' ', sizeof(sysctl_cpu_freqs_txt));
277 cpos = sysctl_cpu_freqs_txt;
278 for (i = 0; i < ncpu_freq_settings; i++) {
279 val = cpu_freq_settings[i].freq;
280 snprintf(cpos, 6, "%d ", val);
281 cpos += (val < 1000) ? 4 : 5;
282 }
283 *cpos = 0;
284
285 error = sysctl_createv(NULL, 0, NULL, &node,
286 CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
287 NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
288 if (error)
289 printf("couldn't create `machdep' node\n");
290
291 error = sysctl_createv(NULL, 0, &node, &cpunode,
292 0, CTLTYPE_NODE, "cpu", NULL,
293 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
294 if (error)
295 printf("couldn't create `cpu' node\n");
296
297 error = sysctl_createv(NULL, 0, &cpunode, &freqnode,
298 0, CTLTYPE_NODE, "frequency", NULL,
299 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
300 if (error)
301 printf("couldn't create `frequency' node\n");
302
303 error = sysctl_createv(NULL, 0, &freqnode, &node,
304 CTLFLAG_READWRITE, CTLTYPE_INT, "target", NULL,
305 sysctl_cpufreq_target, 0, &cpu_freq_target, 0,
306 CTL_CREATE, CTL_EOL);
307 if (error)
308 printf("couldn't create `target' node\n");
309
310 error = sysctl_createv(NULL, 0, &freqnode, &node,
311 0, CTLTYPE_INT, "current", NULL,
312 sysctl_cpufreq_current, 0, NULL, 0,
313 CTL_CREATE, CTL_EOL);
314 if (error)
315 printf("couldn't create `current' node\n");
316
317 error = sysctl_createv(NULL, 0, &freqnode, &node,
318 CTLFLAG_READONLY, CTLTYPE_STRING, "available", NULL,
319 NULL, 0, sysctl_cpu_freqs_txt, 0,
320 CTL_CREATE, CTL_EOL);
321 if (error)
322 printf("couldn't create `available' node\b");
323 }
324
325
326 uint64_t
327 exynos_get_cpufreq(void)
328 {
329 uint32_t reg = 0;
330 uint32_t regval;
331 uint32_t freq;
332
333 #ifdef EXYNOS4
334 if (IS_EXYNOS4_P())
335 reg = EXYNOS4_CMU_APLL + PLL_CON0_OFFSET;
336 #endif
337 #ifdef EXYNOS5
338 if (IS_EXYNOS5_P())
339 reg = EXYNOS5_CMU_APLL + PLL_CON0_OFFSET;
340 #endif
341 KASSERT(reg);
342
343 regval = bus_space_read_4(&exynos_bs_tag, exynos_core_bsh, reg);
344 freq = PLL_FREQ(EXYNOS_F_IN_FREQ, regval);
345
346 return freq;
347 }
348
349
350 static void
351 exynos_set_cpufreq(const struct cpu_freq *freqreq)
352 {
353 struct cpu_info *ci;
354 uint32_t reg = 0;
355 uint32_t regval;
356 int M, P, S;
357 int cii;
358
359 M = freqreq->M;
360 P = freqreq->P;
361 S = freqreq->S;
362
363 regval = __SHIFTIN(M, PLL_CON0_M) |
364 __SHIFTIN(P, PLL_CON0_P) |
365 __SHIFTIN(S, PLL_CON0_S);
366
367 #ifdef EXYNOS4
368 if (IS_EXYNOS4_P())
369 reg = EXYNOS4_CMU_APLL + PLL_CON0_OFFSET;
370 #endif
371 #ifdef EXYNOS5
372 if (IS_EXYNOS5_P())
373 reg = EXYNOS5_CMU_APLL + PLL_CON0_OFFSET;
374 #endif
375 KASSERT(reg);
376
377 /* enable PPL and write config */
378 regval |= PLL_CON0_ENABLE;
379 bus_space_write_4(&exynos_bs_tag, exynos_core_bsh, reg, regval);
380
381 /* update our cycle counter i.e. our CPU frequency for all CPUs */
382 for (CPU_INFO_FOREACH(cii, ci)) {
383 ci->ci_data.cpu_cc_freq = exynos_get_cpufreq();
384 }
385 }
386
387
388 static int
389 sysctl_cpufreq_target(SYSCTLFN_ARGS)
390 {
391 struct sysctlnode node;
392 uint32_t t, curfreq, minfreq, maxfreq;
393 int i, best_i, diff;
394 int error;
395
396 curfreq = exynos_get_cpufreq() / (1000*1000);
397 t = *(int *)rnode->sysctl_data;
398 if (t == 0)
399 t = curfreq;
400
401 node = *rnode;
402 node.sysctl_data = &t;
403 error = sysctl_lookup(SYSCTLFN_CALL(&node));
404 if (error || newp == NULL)
405 return error;
406
407 minfreq = cpu_freq_settings[0].freq;
408 maxfreq = cpu_freq_settings[ncpu_freq_settings-1].freq;
409
410 if ((t < minfreq) || (t > maxfreq))
411 return EINVAL;
412
413 if (t == curfreq) {
414 *(int *)rnode->sysctl_data = t;
415 return 0;
416 }
417
418 diff = maxfreq;
419 best_i = -1;
420 for (i = 0; i < ncpu_freq_settings; i++) {
421 if (abs(t - cpu_freq_settings[i].freq) <= diff) {
422 diff = labs(t - cpu_freq_settings[i].freq);
423 best_i = i;
424 }
425 }
426 if (best_i < 0)
427 return EINVAL;
428
429 exynos_set_cpufreq(&cpu_freq_settings[best_i]);
430
431 *(int *)rnode->sysctl_data = t;
432 return 0;
433 }
434
435
436 static int
437 sysctl_cpufreq_current(SYSCTLFN_ARGS)
438 {
439 struct sysctlnode node = *rnode;
440 uint32_t freq;
441
442 freq = exynos_get_cpufreq() / (1000*1000);
443 node.sysctl_data = &freq;
444
445 return sysctl_lookup(SYSCTLFN_CALL(&node));
446 }
447
448
449 #ifdef VERBOSE_INIT_ARM
450 #define DUMP_PLL(v, var) \
451 reg = EXYNOS##v##_CMU_##var + PLL_CON0_OFFSET;\
452 regval = bus_space_read_4(&exynos_bs_tag, exynos_core_bsh, reg); \
453 freq = PLL_FREQ(EXYNOS_F_IN_FREQ, regval); \
454 printf("%8s at %d Mhz\n", #var, freq/(1000*1000));
455
456
457 static void
458 exynos_dump_clocks(void)
459 {
460 uint32_t reg = 0;
461 uint32_t regval;
462 uint32_t freq;
463
464 printf("Initial PLL settings\n");
465 #ifdef EXYNOS4
466 if (IS_EXYNOS4_P()) {
467 DUMP_PLL(4, APLL);
468 DUMP_PLL(4, MPLL);
469 DUMP_PLL(4, EPLL);
470 DUMP_PLL(4, VPLL);
471 }
472 #endif
473 #ifdef EXYNOS5
474 if (IS_EXYNOS5_P()) {
475 DUMP_PLL(5, APLL);
476 DUMP_PLL(5, MPLL);
477 DUMP_PLL(5, EPLL);
478 DUMP_PLL(5, VPLL);
479 DUMP_PLL(5, CPLL);
480 DUMP_PLL(5, GPLL);
481 DUMP_PLL(5, BPLL);
482 }
483 #endif
484 }
485 #undef DUMP_PLL
486 #endif
487
488
489 void
490 exynos_clocks_bootstrap(void)
491 {
492 #ifdef EXYNOS4
493 if (IS_EXYNOS4_P()) {
494 cpu_freq_settings = cpu_freq_settings_exynos4;
495 ncpu_freq_settings = __arraycount(cpu_freq_settings_exynos4);
496 }
497 #endif
498 #ifdef EXYNOS5
499 if (IS_EXYNOS5_P()) {
500 cpu_freq_settings = cpu_freq_settings_exynos5;
501 ncpu_freq_settings = __arraycount(cpu_freq_settings_exynos5);
502 }
503 #endif
504 KASSERT(ncpu_freq_settings != 0);
505 KASSERT(ncpu_freq_settings < NFRQS);
506
507 #ifdef VERBOSE_INIT_ARM
508 exynos_dump_clocks();
509 #endif
510
511 /* set max cpufreq */
512 exynos_set_cpufreq(&cpu_freq_settings[ncpu_freq_settings-1]);
513 }
514
515
516 void
517 exynos_bootstrap(vaddr_t iobase, vaddr_t uartbase)
518 {
519 int error;
520 size_t core_size, audiocore_size;
521 size_t audiocore_pbase, audiocore_vbase __diagused;
522
523 #ifdef EXYNOS4
524 if (IS_EXYNOS4_P()) {
525 core_size = EXYNOS4_CORE_SIZE;
526 audiocore_size = EXYNOS4_AUDIOCORE_SIZE;
527 audiocore_pbase = EXYNOS4_AUDIOCORE_PBASE;
528 audiocore_vbase = EXYNOS4_AUDIOCORE_VBASE;
529 }
530 #endif
531
532 #ifdef EXYNOS5
533 if (IS_EXYNOS5_P()) {
534 core_size = EXYNOS5_CORE_SIZE;
535 audiocore_size = EXYNOS5_AUDIOCORE_SIZE;
536 audiocore_pbase = EXYNOS5_AUDIOCORE_PBASE;
537 audiocore_vbase = EXYNOS5_AUDIOCORE_VBASE;
538 }
539 #endif
540
541 /* set up early console so we can use printf() and friends */
542 #ifdef EXYNOS_CONSOLE_EARLY
543 uart_base = (volatile uint8_t *) uartbase;
544 cn_tab = &exynos_earlycons;
545 printf("Exynos early console operational\n\n");
546 #endif
547 /* map in the exynos io registers */
548 error = bus_space_map(&exynos_bs_tag, EXYNOS_CORE_PBASE,
549 core_size, 0, &exynos_core_bsh);
550 if (error)
551 panic("%s: failed to map in Exynos SFR registers: %d",
552 __func__, error);
553 KASSERT(exynos_core_bsh == iobase);
554
555 error = bus_space_map(&exynos_bs_tag, audiocore_pbase,
556 audiocore_size, 0, &exynos_audiocore_bsh);
557 if (error)
558 panic("%s: failed to map in Exynos audio SFR registers: %d",
559 __func__, error);
560 KASSERT(exynos_audiocore_bsh == audiocore_vbase);
561
562 /* init bus dma tags */
563 exynos_dma_bootstrap(physmem * PAGE_SIZE);
564
565 /* gpio bootstrapping delayed */
566 }
567
568
569 void
570 exynos_device_register(device_t self, void *aux)
571 {
572 if (device_is_a(self, "armperiph")
573 && device_is_a(device_parent(self), "mainbus")) {
574 /*
575 * XXX KLUDGE ALERT XXX
576 * The iot mainbus supplies is completely wrong since it scales
577 * addresses by 2. The simplest remedy is to replace with our
578 * bus space used for the armcore regisers (which armperiph uses).
579 */
580 struct mainbus_attach_args * const mb = aux;
581 mb->mb_iot = &exynos_bs_tag;
582 return;
583 }
584 if (device_is_a(self, "armgic")
585 && device_is_a(device_parent(self), "armperiph")) {
586 /*
587 * The Exynos4420 armgic is located at a different location!
588 */
589
590 extern uint32_t exynos_soc_id;
591
592 switch (EXYNOS_PRODUCT_ID(exynos_soc_id)) {
593 #if defined(EXYNOS5)
594 case 0xe5410:
595 /* offsets not changed on matt's request */
596 #if 0
597 mpcaa->mpcaa_memh = EXYNOS_CORE_VBASE;
598 mpcaa->mpcaa_off1 = EXYNOS5_GIC_IOP_DISTRIBUTOR_OFFSET;
599 mpcaa->mpcaa_off2 = EXYNOS5_GIC_IOP_CONTROLLER_OFFSET;
600 #endif
601 break;
602 #endif
603 #if defined(EXYNOS4)
604 case 0xe4410:
605 case 0xe4412: {
606 struct mpcore_attach_args * const mpcaa = aux;
607
608 mpcaa->mpcaa_memh = EXYNOS_CORE_VBASE;
609 mpcaa->mpcaa_off1 = EXYNOS4_GIC_DISTRIBUTOR_OFFSET;
610 mpcaa->mpcaa_off2 = EXYNOS4_GIC_CNTR_OFFSET;
611 break;
612 }
613 #endif
614 default:
615 panic("%s: unknown SoC product id %#x", __func__,
616 (u_int)EXYNOS_PRODUCT_ID(exynos_soc_id));
617 }
618 return;
619 }
620 if (device_is_a(self, "armgtmr") || device_is_a(self, "mct")) {
621 #ifdef EXYNOS5
622 /*
623 * The global timer is dependent on the MCT running.
624 */
625 bus_size_t o = EXYNOS5_MCT_OFFSET + MCT_G_TCON;
626 uint32_t v = bus_space_read_4(&exynos_bs_tag, exynos_core_bsh,
627 o);
628 v |= G_TCON_START;
629 bus_space_write_4(&exynos_bs_tag, exynos_core_bsh, o, v);
630 #endif
631 /*
632 * The frequencies of the timers are the reference
633 * frequency.
634 */
635 prop_dictionary_set_uint32(device_properties(self),
636 "frequency", EXYNOS_F_IN_FREQ);
637 return;
638 }
639
640 exyo_device_register(self, aux);
641 }
642
643
644 void
645 exynos_device_register_post_config(device_t self, void *aux)
646 {
647 exyo_device_register_post_config(self, aux);
648 }
649
650