On Tue, Apr 26, 2011 at 1:48 PM, Barry Song 21cnbao@gmail.com wrote:
2011/4/26 Barry Song 21cnbao@gmail.com:
Hi Michael,
2011/4/26 Michael Hope michael.hope@linaro.org:
Hi Barry. I think the toolchain is operating correctly here. The current version recognises a divide followed by a modulo and optimises this into a call to the standard EABI function __aeabi__uldivmod(). Note the code:
do_div(Kpart, source);
K = Kpart & 0xFFFFFFFF;
/* Check if we need to round */ if ((K % 10) >= 5) K += 5;
This function is provided by libgcc for normal applications. The kernel provides it's own versions in arch/arm/lib/lib1funcs.s but is missing __aeabi_uldivmod (note the 'l' for 64 bit).
In fact the problem happen ealier:
if (Ndiv < 6) { source /= 2; pll_div->pre_div = 1; Ndiv = target / source; } else pll_div->pre_div = 0;
if ((Ndiv < 6) || (Ndiv > 12)) printk(KERN_WARNING "WM8974 N value %u outwith recommended range!\n", Ndiv);
pll_div->n = Ndiv; Nmod = target % source; Kpart = FIXED_PLL_SIZE * (long long)Nmod;
do_div(Kpart, source);
If commenting "source /= 2", the problem disappear.
Or if adding one line before do_div, all will be ok. asm("" : "+r"(source)); do_div(Kpart, source);
Hi Barry. I can reproduce the problem in Linaro GCC 4.5-2011.04 and GCC 4.5.2. It does not exist in GCC 4.6.0. wm8974_set_dai_pll() inlines the call to pll_factors() and leaves a .globl reference to __aeabi_uldivmod in the code. This function is never called.
Marking pll_factors() as __attribute__((noinline)) also works around the problem.
I've logged http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48783 upstream and recorded this as LP: #771551.
-- Michael