From: John M. <gi...@gi...> - 2014-01-30 06:25:48
|
mesa-hostmot2 driver: Remove int->double conversions Fixes 'Unknown symbol __fixunsdfdi' on 32-bit gcc-4.[34].x kthreads For 64-bit arithmetic, gcc provides helpers on 32-bit architectures. The __fixunsdfdi routine converts a double to u64. For some versions of gcc, this symbol is left for the linker to resolve from libgcc.a. Kbuild does not link from libgcc.a, so these must be worked around. The sserial.c example casts the double pin->fullscale to a float so the expression is evaluated as a float, and then casts the float to u32, avoiding double and u64 types in the conversion. The watchdog.c example replaces the floating point calculation, presumably meant to avoid loss of precision in an integer divide, by doing the multiplication in 64-bit space first, then using the kernel-supplied do_div() macro for the 64-bit integer division. Also see these links for where these issues were addressed previously: http://permalink.gmane.org/gmane.linux.distributions.emc.user/18718 http://permalink.gmane.org/gmane.linux.distributions.emc.devel/3663 (There are several more places in components/lcd.c parse_fmt() that need these fixes; this patch at least the hostmot2 driver and unit tests working.) http://git.linuxcnc.org/?p=linuxcnc.git;a=commitdiff;h=b9456dc --- src/hal/drivers/mesa-hostmot2/sserial.c | 10 ++++++++-- src/hal/drivers/mesa-hostmot2/watchdog.c | 13 ++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/hal/drivers/mesa-hostmot2/sserial.c b/src/hal/drivers/mesa-hostmot2/sserial.c index f032c9d..72f0d18 100644 --- a/src/hal/drivers/mesa-hostmot2/sserial.c +++ b/src/hal/drivers/mesa-hostmot2/sserial.c @@ -1279,8 +1279,14 @@ void hm2_sserial_prepare_tram_write(hostmot2_t *hm2, long period){ val = *pin->float_pin; if (val > pin->maxlim) val = pin->maxlim; if (val < pin->minlim) val = pin->minlim; - buff = (u64)((val / pin->fullscale) - * (~0ull >> (64 - conf->DataLength))); + /* convert to u32 before u64 to + avoid needing __fixunsdfdi() in + libgcc.a from some gccs on 32-bit + arches + */ + buff = (u64)(u32) + ((val / (float)pin->fullscale) + * (~0ull >> (64 - conf->DataLength))); break; case LBP_SIGNED: //this only works if DataLength <= 32 diff --git a/src/hal/drivers/mesa-hostmot2/watchdog.c b/src/hal/drivers/mesa-hostmot2/watchdog.c index 412e92b..d61739a 100755 --- a/src/hal/drivers/mesa-hostmot2/watchdog.c +++ b/src/hal/drivers/mesa-hostmot2/watchdog.c @@ -309,7 +309,18 @@ void hm2_watchdog_force_write(hostmot2_t *hm2) { // watchdog is disabled, MSb=1 is secret handshake with FPGA hm2->watchdog.timer_reg[0] = 0x80000000; } else { - tmp = (hm2->watchdog.instance[0].hal.param.timeout_ns * ((double)hm2->watchdog.clock_frequency / (double)(1000 * 1000 * 1000))) - 1; +#ifdef __KERNEL__ + /* some gccs don't include 64-bit integer functions until + linking with gcc, but kbuild links directly with ld; instead, + use kernel-supplied functions; in this case, replace + __udivdi3 with do_div */ + tmp = ((u64)hm2->watchdog.instance[0].hal.param.timeout_ns * + hm2->watchdog.clock_frequency); + do_div(tmp,(u64)(1000 * 1000 * 1000)) - 1; +#else + tmp = ((u64)hm2->watchdog.instance[0].hal.param.timeout_ns * + hm2->watchdog.clock_frequency / (1000 * 1000 * 1000)) - 1; +#endif if (tmp < 0x80000000) { hm2->watchdog.timer_reg[0] = tmp; } else { |