[PATCH 2/4] Add cpufreq driver for the IBM PowerPC 750GX

Kevin Diggs kevdig at hypersurf.com
Tue Aug 26 10:57:32 EST 2008


Arnd Bergmann wrote:
> On Monday 25 August 2008, Kevin Diggs wrote:
> 
>>+ * cf750gx.c - cpufreq driver for the dual PLLs in the 750gx
> 
> 
> Thanks for posting this driver and for your attention for detail
> and for documentation in particular. Few people bother to write
> documentation at this level.
> 
> I don't understand enough of cpufreq or your hardware to comment
> on that, but please let me give you a few hints on coding style.
> 
> 
>>+ *  Copyright (C) 2008       kevin Diggs
> 
> 
> Most people list their email address here as well
> 
For reasons I'd rather not go into, my email address is not likely
to remain valid for much longer.
> 
>>+#define cf750gxmChangingPll            (0x80000000)
>>+#define cf750gxmChangingPllBit         (31)
>>+#define cf750gxmTurningIdlePllOff      (0x40000000)
>>+#define cf750gxmTurningIdlePllOffBit   (30)
> 
> 
> constants should be ALL_CAPS, not sIllYCaPS.
> 
Are cf750gxm_CHANGING_PLL and cf750gxm_CHANGING_PLL_BIT_POS ok?
> 
>>+struct pll_750fgx_t {
>>+       unsigned short min_ratio;       /* min bus ratio */
>>+       unsigned short max_ratio;       /* max bus ratio */
>>+       unsigned int min_core;          /* min core frequency per spec (KHz) */
>>+       unsigned int max_core;          /* max core frequency per spec (KHz) */
>>+};
> 
> 
> please drop the _t at the end of the identifier.
> 
done
> 
>>+MODULE_AUTHOR("Kevin Diggs");
>>+MODULE_DESCRIPTION("750GX Dual PLL cpufreq driver");
>>+MODULE_LICENSE("GPL");
> 
> 
> Move this to the end.
> 
done
> 
>>+struct cf750gx_t_call_data {
>>+       struct cpufreq_freqs freqs;
>>+       unsigned long current_pll;
>>+       int idle_pll_off;
>>+};
> 
> 
> drop the _t here, or make explicit what is meant by it.
> 
Now that I look at it the _t is supposed to be at the end. It is
meant to indicate that this is a structure tag or type. I'll
remove it.
> 
>>+static const struct pll_750fgx_t __initdata pll_750fx = {
>>+       .min_ratio = 2,
>>+       .max_ratio = 20,
>>+       .min_core = 400000,
>>+       .max_core = 800000,
>>+};
>>+
>>+static const struct pll_750fgx_t __initdata pll_750gx = {
>>+       .min_ratio = 2,
>>+       .max_ratio = 20,
>>+       .min_core = 500000,
>>+       .max_core = 1000000,
>>+};
> 
> 
> Are these correct on any board? If they can be different
> depending on the board design, it would be better to get
> this data from the device tree.
> 
They are a spceification of the processor itself. Should be
the same for any board using the 750GX (or FX).
> 
>>+static DECLARE_COMPLETION(cf750gx_v_exit_completion);
>>+
>>+static unsigned int override_min_core = 0;
>>+static unsigned int override_max_core = 0;
>>+static unsigned int minmaxmode = 0;
>>+
>>+static unsigned int cf750gx_v_min_core = 0;
>>+static unsigned int cf750gx_v_max_core = 0;
>>+static int cf750gx_v_idle_pll_off = 0;
>>+static int cf750gx_v_min_max_mode = 0;
>>+static unsigned long cf750gx_v_state_bits = 0;
> 
> 
> Is 0 a meaningful value for these? If it just means 'uninitialized',
> then better don't initialize them in the first place, for clarity.
> 
The first 3 are module parameters. For the first 2, 0 means
that they were not set. minmaxmode is a boolean. 0 is the
default of disabled.

When I was initially writing the code I figured I would
need the min and max core frequencies in several places.
As it turns out they are only used in the code
initialization routine (cf750gx_init()). I have made
them locals.

..._idle_pll_off is a boolean for a sysfs attribute. 0 is
the default of disabled.

..._min_max_mode is a boolean to hold the state of
minmaxmode. Seems to be only used to print the current
value.

..._state_bits is a global to maintain state.

Does the PowerPC suffer any performance penalties when
accessing shorts compared to ints? Can I save a few bytes
by using shorts?
> 
>>+static struct cpufreq_frequency_table *cf750gx_v_f_table;
>>+static struct cpufreq_frequency_table *cf750gx_v_freq_table;
>>+static struct cpufreq_frequency_table *cf750gx_v_min_max_freq_table;
>>+
>>+static struct cf750gx_t_call_data cf750gx_v_switch_call_data;
>>+static struct cf750gx_t_call_data cf750gx_v_lock_call_data;
>>+static struct notifier_block cf750gx_v_pll_switch_nb;
>>+static struct notifier_block cf750gx_v_pll_lock_nb;
> 
> 
> Also, in general, try to avoid global variables here, even 
> in file scope (static), but rather put all device specific
> data into a per-device data structure.
> 
How big of a problem is this? I regret the decision to rip
the SMP stuff out. But it is kinda done. If absolutely
necessary I can put these into a structure?
> 
>>+static int cf750gx_pll_switch_cb(struct notifier_block *nb, unsigned long
>>+       action, void *data)
>>+{
>>+struct cf750gx_t_call_data *cd;
>>+unsigned int idle_pll;
>>+unsigned int pll_off_cmd;
>>+unsigned int new_pll;
> 
> 
> The whitespace appears damaged here.
> 
Just a coding style thing. I put declarations (or definitions -
I get the two confused?) on the same indent as the block they are
in. Is this a 15 yard illegal procedure penalty?
> 
>>+       cd = (struct cf750gx_t_call_data *)data;
> 
done
> 
> data is a void pointer, so you don't need the cast, and shouldn't
> have it therefore.
> 
>>+static int cf750gx_pll_lock_cb(struct notifier_block *nb, unsigned long action,
>>+       void *data)
>>+{
>>+struct cf750gx_t_call_data *cd;
>>+
>>+       cd = (struct cf750gx_t_call_data *)data;
> 
> 
> same here.
> 
and done
> 
>>+static int cf750gx_target(struct cpufreq_policy *policy,
>>+                              unsigned int target_freq, unsigned int relation)
>>+{
>>+unsigned int next_index = 0;           /* Index into freq_table */
>>+unsigned int next_freq = 0;            /* next frequency from perf table */
>>+unsigned int next_perf_state = 0;      /* Index from perf table */
>>+int result = 0;
> 
> 
> Don't initialize local variables in the declaration, as that will prevent
> the compiler from warning about uninitialized use.
> 
done
> 
>>+unsigned int pll;
>>+unsigned int new_pll;
>>+unsigned int active_pll;
>>+struct cpufreq_freqs freqs;
>>+struct cpufreq_frequency_table *ft = cf750gx_v_f_table;
> 
> 
> more whitespace damage. Maybe there is something wrong with your
> text editor.
> 
Nope, just a faulty programmer ...
> 
>>+       dprintk(__FILE__">%s(, %u KHz, relation %u)-%d:  on cpu %d\n",
>>+               __func__, target_freq, relation, __LINE__, policy->cpu);
>>+
>>+       if (test_and_set_bit(cf750gxmChangingPllBit, &cf750gx_v_state_bits))
>>+               return -EAGAIN;
>>+
>>+       INIT_COMPLETION(cf750gx_v_exit_completion);
>>+
>>+       result = cpufreq_frequency_table_target(policy,
>>+                                               ft,
>>+                                               target_freq,
>>+                                               relation, &next_index);
>>+
>>+       if (unlikely(result))
>>+               goto cf750gxTargetNoFreq;
> 
> 
> The unlikely() here looks like overoptimization, drop it in favor of
> readability unless you can measure a real-world difference.
> 
This was stolen from the ACPI Processor P-States Driver. Given the
frequency of calls I would guess it does not make a difference.
> 
>>+       if (active_pll) {
>>+       unsigned int current_state;
> 
> 
> whitespace damage.
> 
same here ...
> 
>>+       dprintk(__FILE__">%s()-%d:  Modifying PLL:  0x%x\n", __func__, __LINE__,
>>+               new_pll);
> 
> 
> Please go through all your dprintk and see if you really really need all of them.
> Usually they are useful while you are doing the initial code, but only get in the
> way as soon as it starts working.
> 
This from a code readability standpoint? Or an efficiency one?
I think the cpufreq stuff has a debug configure option that
disables compilation of these unless enabled.
> 
>>+cf750gxTargetOut:
>>+       return result;
>>+
>>+cf750gxTargetNoFreq:
>>+       result = -ENODEV;
>>+
>>+       goto cf750gxTargetUnlock;
>>+cf750gxTargetFreqSet:
>>+       result = 0;
>>+
>>+       goto cf750gxTargetUnlock;
>>+cf750gxTargetUnlock:
>>+       clear_bit(cf750gxmChangingPllBit, &cf750gx_v_state_bits);
>>+       complete(&cf750gx_v_exit_completion);
>>+
>>+       goto cf750gxTargetOut;
> 
> 
> The conventional way to write this would be:
> 
> 	result = -ENODEV;
> 	if (foo)
> 		goto out_unlock;
> 
> 	result = 0;
> 	if (bar)
> 		goto out_unlock;
> 
> 	return 0;
> 
> out_unlock:
> 	clear_bit(cf750gxmChangingPllBit, &cf750gx_v_state_bits);
> 	complete(&cf750gx_v_exit_completion);
> out:
> 	return result;
> 
I'll fix this.
> 
>>+/*     policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; */
>>+       policy->cpuinfo.transition_latency = pllif_get_latency();
> 
> 
> The comment does not really explain anything. If you just want to disable
> code, use #if 0, but better drop it right away and add a comment about
> what might need changing.
> 
deleted.
> 
>>+       result = cpufreq_frequency_table_cpuinfo(policy, cf750gx_v_f_table);
>>+       if (result)
>>+               goto err_freqfree;
>>+
>>+       cpufreq_frequency_table_get_attr(cf750gx_v_f_table, policy->cpu);
>>+
>>+       cf750gx_v_pll_switch_nb.notifier_call = cf750gx_pll_switch_cb;
>>+       cf750gx_v_pll_switch_nb.next = (struct notifier_block *)
>>+               &cf750gx_v_switch_call_data;
>>+       cf750gx_v_pll_switch_nb.priority = 0;
>>+
>>+       result = pllif_register_pll_switch_cb(&cf750gx_v_pll_switch_nb);
>>+
>>+       cf750gx_v_pll_lock_nb.notifier_call = cf750gx_pll_lock_cb;
>>+       cf750gx_v_pll_lock_nb.next =
>>+               (struct notifier_block *)&cf750gx_v_lock_call_data;
> 
> 
> These casts look wrong, cf750gx_v_lock_call_data is not a notifier_block.
> What are you trying to do here?
> 
Just a little sneaky. I should document in the kernel doc though.
> 
>>+       cf750gx_v_pll_lock_nb.priority = 0;
>>+
>>+       result = pllif_register_pll_lock_cb(&cf750gx_v_pll_lock_nb);
>>+
>>+       return result;
>>+
>>+err_freqfree:
>>+       return result;
>>+}
> 
> 
> The first 'return result' is redundant, drop it.
> 
done.
> 
>>+
>>+static int cf750gx_cpu_exit(struct cpufreq_policy *policy)
>>+{
>>+       dprintk("%s()\n", __func__);
>>+
>>+       /*
>>+        * Wait for any active requests to ripple through before exiting
>>+        */
>>+       wait_for_completion(&cf750gx_v_exit_completion);
> 
> 
> This "wait for anything" use of wait_for_completion looks wrong, 
> because once any other function has done the 'complete', you won't
> wait here any more.
> 
> What exactly are you trying to accomplish with this?
> 
Originall I had something like:

	while(some_bit_is_still_set)
		sleep

I think you suggested I use a completion because it is in
fact simpler than a sleep. Now that I think about it also seems
intuitive to give the system a hint as to when something will
be done. 'complete' just means there is no timer pending (unless,
of course, I screwed up the code).
> 
>>+static int __init cf750gx_init(void)
>>+{
>>+int ret;
>>+unsigned int freq, i, j, rng, bus_clock;
>>+unsigned short min_ratio, max_ratio;
>>+struct cpufreq_frequency_table *tbp;
>>+const struct pll_750fgx_t *pll_defaults;
> 
> 
> whitespace.
> 
> 
>>+       dprintk("%s()\n", __func__);
>>+
>>+       if (!cpu_has_feature(CPU_FTR_DUAL_PLL_750FX))
>>+               return 0;
> 
> 
> Is this purely a feature of the CPU or does it need logic
> in the board design? If you need external hardware for it,
> you need to check the device tree for the presence of that
> hardware.
> 
Purely a feature of the CPU. From what I know, voltage scaling
is an important part of reducing power comsumption. That would
be platform specific code. Anyone know if this is possible for
any 750GX based system? As far as I know it is not possible on
mine?
> 
>>+       if (cf750gx_v_freq_table == NULL) {
>>+               ret = -ENOMEM;
>>+               goto ErrSimple;
>>+       }
> 
> 
> ret = -ENOMEM;
> if (!cf750gx_freq_table)
> 	goto err_simple;
> 
done
> 
> 	Arnd <><
> 
> 




More information about the Linuxppc-dev mailing list