* Re: Efficient memcpy()/memmove() for G2/G3 cores...
From: Matt Sealey @ 2008-08-25 11:00 UTC (permalink / raw)
To: David Jander; +Cc: linuxppc-dev
In-Reply-To: <200808251131.02071.david.jander@protonic.nl>
Hi David,
The focus has definitely been on VMX but that's not to say lower power
processors were forgotten :)
Gunnar von Boehn did some benchmarking with an assembly optimized routine,
for Cell, 603e and so on (basically the whole gamut from embedded up to
sever class IBM chips) and got some pretty good results;
http://www.powerdeveloper.org/forums/viewtopic.php?t=1426
It is definitely something that needs fixing. The generic routine in glibc
just copies words with no benefit of knowing the cache line size or any
cache block buffers in the chip, and certainly no use of cache control or
data streaming on higher end chips.
With knowledge of the right way to unroll the loops, how many copies to
do at once to try and get a burst, reducing cache usage etc. you can get
very impressive performance (as you can see, 50MB up to 78MB at the
smallest size, the basic improvement is 2x performance).
I hope that helps you a little bit. Gunnar posted code to this list not
long after. I have a copy of the "e300 optimized" routine but I thought
best he should post it here, than myself.
There is a lot of scope I think for optimizing several points (glibc,
kernel, some applications) for embedded processors which nobody is
really taking on. But, not many people want to do this kind of work..
--
Matt Sealey <matt@genesi-usa.com>
Genesi, Manager, Developer Relations
David Jander wrote:
> Hello,
>
> I was wondering if there is a good replacement for GLibc memcpy() functions,
> that doesn't have horrendous performance on embedded PowerPC processors (such
> as Glibc has).
>
> I did some simple benchmarks with this implementation on our custom MPC5121
> based board (Freescale e300 core, something like a PPC603e, G2, without VMX):
>
> ...
> unsigned long int a,b,c,d;
> unsigned long int a1,b1,c1,d1;
> ...
> while (len >= 32)
> {
> a = plSrc[0];
> b = plSrc[1];
> c = plSrc[2];
> d = plSrc[3];
> a1 = plSrc[4];
> b1 = plSrc[5];
> c1 = plSrc[6];
> d1 = plSrc[7];
> plSrc += 8;
> plDst[0] = a;
> plDst[1] = b;
> plDst[2] = c;
> plDst[3] = d;
> plDst[4] = a1;
> plDst[5] = b1;
> plDst[6] = c1;
> plDst[7] = d1;
> plDst += 8;
> len -= 32;
> }
> ...
>
> And the results are more than telling.... by linking this with LD_PRELOAD,
> some programs get an enourmous performance boost.
> For example a small test program that copies frames into video memory (just
> RAM) improved throughput from 13.2 MiB/s to 69.5 MiB/s.
> I have googled for this issue, but most optimized versions of memcpy() and
> friends seem to focus on AltiVec/VMX, which this processor does not have.
> Now I am certain that most of the G2/G3 users on this list _must_ have a
> better solution for this. Any suggestions?
>
> Btw, the tests are done on Ubuntu/PowerPC 7.10, don't know if that matters
> though...
>
> Best regards,
>
^ permalink raw reply
* gdb problems with threads on mpc512x
From: Daniele Bosi @ 2008-08-25 10:33 UTC (permalink / raw)
To: linuxppc-dev
[-- Attachment #1: Type: text/html, Size: 2602 bytes --]
^ permalink raw reply
* [PATCH 2/4] Add cpufreq driver for the IBM PowerPC 750GX
From: Kevin Diggs @ 2008-08-25 10:53 UTC (permalink / raw)
To: linuxppc-dev, linux-kernel
This adds the actual cpufreq driver for the 750GX. It supports all integer
ratios that are valid for the processor model and bus frequency.
It has two modes of operation. Normal mode uses all valid frequencies. In
minmaxmode, only the minimum and maximum are used. This provides the ability
for very low latency frequency switches.
There is also a sysfs attribute to have it switch off the unused PLL for
additional power savings.
This does NOT support SMP.
My name is Kevin Diggs and I approve this patch.
Signed-off-by: Kevin Diggs <kevdig@hypersurf.com>
Index: Documentation/DocBook/cf750gx.tmpl
===================================================================
--- /dev/null 2004-08-10 18:55:00.000000000 -0700
+++ Documentation/DocBook/cf750gx.tmpl 2008-08-20 10:00:14.000000000 -0700
@@ -0,0 +1,441 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
+
+<book id="ppc750gx-cpufreq-guide">
+ <bookinfo>
+ <title>PowerPC 750GX (and FX?) cpufreq driver guide</title>
+
+ <authorgroup>
+ <author>
+ <firstname>Kevin</firstname>
+ <surname>Diggs</surname>
+ </author>
+ </authorgroup>
+
+ <revhistory>
+ <revision>
+ <revnumber>1.0 </revnumber>
+ <date>August 12, 2008</date>
+ <revremark>Initial revision posted to linuxppc-dev</revremark>
+ </revision>
+ </revhistory>
+
+ <copyright>
+ <year>2008</year>
+ <holder>Kevin Diggs</holder>
+ </copyright>
+
+ <legalnotice>
+ <para>
+ This documentation is free software; you can redistribute
+ it and/or modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later
+ version.
+ </para>
+
+ <para>
+ This program is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details.
+ </para>
+
+ <para>
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ MA 02111-1307 USA
+ </para>
+
+ <para>
+ For more details see the file COPYING in the source
+ distribution of Linux.
+ </para>
+ </legalnotice>
+
+ <releaseinfo>
+ This is the first release of this document coincident with submission of the
+ driver for inclusion in the kernel.
+ </releaseinfo>
+
+ </bookinfo>
+
+ <toc></toc>
+
+ <chapter id="introduction">
+ <title>Introduction</title>
+ <para>
+ This guide documents the cpufreq driver for the IBM PowerPC 750GX processor.
+ It "should" also work with the 750FX but has not been tested.
+ </para>
+ <para>
+ The driver is split into two main parts. The first provides the low level
+ interface to the PLL configuration register (HID1 - SPR 1009). It is called
+ pll_if. The second is the actual cpufreq driver, called cf750gx. The pll_if
+ component handles the details of dealing with the PLL like PLL lock delay
+ requirements and preventing illegal operations. The cf750gx driver provides
+ the interface to the cpufreq subsystem. Under control of the specified
+ governor it will generate the required commands to switch the processor
+ frequency as requested and send them to pll_if to carry them out.
+ </para>
+ </chapter>
+
+ <chapter id="MajorComponents">
+ <title>Major Components</title>
+
+ <para>
+ The IBM 750GX (and FX) processor has a pair of PLLs that can be programmed to
+ operate at a number of different frequencies. The frequencies are specified
+ as ratios to the system bus and range from 2 to 20. From 2 to 10 it also
+ supports half ratios (i.e. 2.5, 3.5) though they are not supported in the
+ cpufreq driver due to a limitation of not being able to switch from one half
+ ratio directly to another. It takes 100 usec for a PLL to relock to a
+ new frequency before it can be used [750GX_ds2-17-05.pdf, Table 3-7,
+ page 18]. It takes 3 bus clocks for the cpu to switch from one PLL to
+ another [750GX_ds2-17-05.pdf, paragraph 3, page 44].
+ </para>
+
+ <para>
+ The cpufreq driver consists of two main parts:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ cf750gx - the cpufreq driver module
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ pll_if - a low level interface that encapsulates the details of dealing
+ with the PLL register
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <sect1 id="cf750gx">
+ <title>cf750gx - The CPUFreq 750GX driver</title>
+
+ <para>
+ cf750gx provides the standard entry points required of a cpufreq driver. They
+ are:
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ cf750gx_verify - verify
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ cf750gx_target - target
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ cf750gx_cpu_init - cpu init
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ cf750gx_cpu_exit - cpu exit
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ These routines perform functions as required by the cpufreq subsystem.
+ </para>
+
+ <para>
+ The driver functions in one of 2 modes: normal and minmax. In normal mode
+ it switches between the available frequencies as requested by the current
+ cpufreq subsystem governor. In minmax mode, it switches between the minimum
+ and maximum frequencies only.
+ </para>
+
+ <sect2 id="cf750gxModesNormal"><title>Normal Mode</title>
+ <para>
+ In normal mode the driver switches between all valid frequencies under
+ control of the selected governor. The list of valid frequencies is
+ determined at startup based on the cpu model (FX or GX) and the determined
+ bus frequency. The cpu model determines the minimum and maximum core
+ frequencies, though these can be overriden at start up via module
+ parameters. See Module Parameters. The bus frequency is obtained from
+ pll_if.
+ </para>
+ </sect2>
+
+ <sect2 id="cf750gxModesMinMax"><title>MinMax Mode</title>
+ <para>
+ In minmax mode the driver switches between the minimum and maximum core
+ frequencies only. This provides the capability for very low latency
+ frequency switches. See Module Parameters and sysfs Attributes.
+ </para>
+ </sect2>
+
+ <sect2 id="cf750gxModuleParameters"><title>Module Parameters</title>
+ <para>
+ cf750gx has 3 module parameters:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ override_max_core
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ override_min_core
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ minmaxmode
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <sect3 id="cf750gxParamOverrideMaxCore"><title>override_max_core</title>
+ <para>
+ Normally the maximum frequency the cpu can be clocked is defined by the
+ cpu model. For the FX it is 800 MHz and 1,000 MHz for the GX. This
+ parameter allows you to override this maximum. It is specified in KHz.
+ </para>
+ </sect3>
+
+ <sect3 id="cf750gxParamOverrideMinCore"><title>override_min_core</title>
+ <para>
+ Likewise for cpu minimum frequency.
+ </para>
+
+ <note>
+ <para>
+ The 750GX on my PowerForce 750GX cpu upgrade seems stable at 250 MHz.
+ The spec says the minimum for the GX is 500 MHz.
+ </para>
+ </note>
+ </sect3>
+
+ <sect3 id="cf750gxParamMinMaxMode"><title>minmaxmode</title>
+ <para>
+ This boolean parameter allows one to enable minmax mode on startup.
+ </para>
+ </sect3>
+ </sect2>
+
+ <sect2 id="cf750gxsysfsAttributes"><title>sysfs Attributes</title>
+ <para>
+ cf750gx has 2 sysfs attributes that can be used to modify its operation.
+ They are:
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ idle_pll_off
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ min_max_mode
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ Both are booleans.
+ </para>
+
+ <sect3 id="IdlePllOffAttr"><title>idle_pll_off</title>
+ <para>
+ This boolean tells the driver to switch the idle PLL (the one NOT
+ currently used to drive the cpu core clock) off. Turning the unused PLL
+ off results in a modest power savings [750GX_ds2-17-05.pdf,
+ section 5.2.1, page 44].
+ </para>
+ </sect3>
+
+ <sect3 id="MinMaxModeAttr"><title>min_max_mode</title>
+ <para>
+ This attribute controls the same feature as the similarly named module
+ parameter.
+ </para>
+
+ <note>
+ <para>
+ In order to get this to work with the conservative governor, the
+ "freq_step" attribute must be set to a value that will pick the max
+ frequency from the 2 element frequency table. A good value is 80 (which
+ says to jump in frequency by 80% of the maximum on each change).
+ </para>
+ </note>
+ </sect3>
+ </sect2>
+ </sect1>
+
+ <sect1 id="pll_if">
+ <title>pll_if - The Low Level PLL Interface Module</title>
+ <para>
+ pll_if provides the choice of two interfaces to control the PLL register.
+ One is via a sysfs attribute. The other is through an exported, public
+ routine. At least one interface must be selected during kernel
+ configuration.
+ </para>
+
+ <sect2 id="sysfsInterface"><title>sysfs Interface - ppc750gxpll</title>
+ <para>
+ This interface provides a sysfs attribute that can be written to to
+ directly control the PLL configuration register. The upper 8 or so bits of
+ the PLL configuration register are read only. This interface takes
+ advantage of this by using these bits to define which of the writeable
+ bits contain valid data. The header file
+ <filename class="headerfile">include/asm-powerpc/pll_if.h</filename>
+ defines the following macros:
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ PLL0_DO_CFG - PLL 0 config (ratio) valid
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ PLL0_DO_RNG - PLL 0 range valid
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ PLL1_DO_CFG - PLL 1 config (ratio) valid
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ PLL1_DO_RNG - PLL 1 range valid
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ PLL_DO_SEL - PLL selection bit valid
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ See the PERL script pll.pl for an example of using the sysfs attribute.
+ </para>
+ </sect2>
+
+ <sect2 id="PublicAPI"><title>Exported, Public Functions Interface</title>
+ <para>
+ This interface provides a public API that other code can use. The list of
+ functions is:
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ pllif_pack_state
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ pllif_get_latency
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ pllif_modify_PLL
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ pllif_get_bus_clock
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ pllif_cfg_to_freq
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ pllif_register_pll_switch_cb
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ pllif_unregister_pll_switch_cb
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ pllif_register_pll_lock_cb
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ pllif_unregister_pll_lock_cb
+ </para>
+ </listitem>
+ </itemizedlist>
+
+
+ The embedded documentation from
+ <filename class="headerfile">include/asm-powerpc/pll_if.h</filename>
+ and
+ <filename class="headerfile">arch/powerpc/kernel/cpu/pll_if.c</filename>
+ is included in the appendix.
+
+ </para>
+ </sect2>
+
+ <sect2 id="pllifModuleParameters"><title>Module Parameters</title>
+ <para>
+ pll_if has one tunable parameter. override_bus_clock allows you to specify
+ the bus frequency. This is useful if the code can not determine the value
+ or gets it wrong. It is in KHz.
+ </para>
+
+ <note>
+ <para>
+ This code was developed on a PowerMac 8600 with a PowerLogix 750GX
+ upgrade card in it. The method used in the code to get the bus frequency
+ is what worked on this system.
+ </para>
+ </note>
+ </sect2>
+ </sect1>
+ </chapter>
+
+ <chapter id="Misc"><title>Misc</title>
+ <sect1 id="pll.pl"><title>pll.pl - A User Friendly pll_if sysfs utility</title>
+ <para>
+ pll.pl is a PERL script that provides a user friendly interface to the
+ pll_if sysfs attribute, cf750gxpll. It provides options to query the
+ current PLL state, change the configuration of either PLL (when inactive),
+ and to switch the PLL in use. See the embedded documentation in pll.pl
+ for details.
+ </para>
+ </sect1>
+ </chapter>
+ <chapter id="Appendix"><title>Appendix</title>
+!Finclude/asm-powerpc/pll_if.h pllifGetLatency pllifPackState
+!Earch/powerpc/kernel/cpu/pll_if.c
+ </chapter>
+</book>
Index: Documentation/DocBook/Makefile
===================================================================
--- Documentation/DocBook/Makefile.orig 2008-08-13 02:18:51.000000000 -0700
+++ Documentation/DocBook/Makefile 2008-08-14 02:47:33.000000000 -0700
@@ -12,7 +12,7 @@ DOCBOOKS := wanbook.xml z8530book.xml mc
kernel-api.xml filesystems.xml lsm.xml usb.xml kgdb.xml \
gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \
genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \
- mac80211.xml debugobjects.xml
+ mac80211.xml debugobjects.xml cf750gx.xml
###
# The build process is as follows (targets):
Index: arch/powerpc/kernel/cpu/cpufreq/Kconfig
===================================================================
--- /dev/null 2004-08-10 18:55:00.000000000 -0700
+++ arch/powerpc/kernel/cpu/cpufreq/Kconfig 2008-08-19 00:24:15.000000000 -0700
@@ -0,0 +1,33 @@
+#
+# CPU Frequency scaling
+#
+
+menu "CPU Frequency scaling"
+
+source "drivers/cpufreq/Kconfig"
+
+if CPU_FREQ
+
+comment "CPUFreq processor drivers"
+
+config CPU_FREQ_PPC_750GX_DUAL_PLL
+ tristate "PowerPC 750 FX/GX Dual PLL driver"
+ depends on !SMP
+ select PPC_750GX_DUAL_PLL_IF
+ select CPU_FREQ_TABLE
+ help
+ This driver adds a CPUFreq driver which utilizes the dual
+ PLLs found in the IBM 750 FX & GX cpus. This will automatically
+ select the pll_if module. It does NOT support SMP.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cf750gx.
+
+ For details, take a look at <file:Documentation/cpu-freq/> and also
+ see the cf750gx DocBook.
+
+ If in doubt, say N.
+
+endif # CPU_FREQ
+
+endmenu
Index: arch/powerpc/kernel/cpu/cpufreq/Makefile
===================================================================
--- /dev/null 2004-08-10 18:55:00.000000000 -0700
+++ arch/powerpc/kernel/cpu/cpufreq/Makefile 2008-08-14 02:44:30.000000000 -0700
@@ -0,0 +1 @@
+obj-$(CONFIG_CPU_FREQ_PPC_750GX_DUAL_PLL) += cf750gx.o
Index: arch/powerpc/kernel/cpu/cpufreq/cf750gx.c
===================================================================
--- /dev/null 2004-08-10 18:55:00.000000000 -0700
+++ arch/powerpc/kernel/cpu/cpufreq/cf750gx.c 2008-08-22 15:54:48.000000000 -0700
@@ -0,0 +1,736 @@
+/*
+ * cf750gx.c - cpufreq driver for the dual PLLs in the 750gx
+ *
+ * Copyright (C) 2008 kevin Diggs
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/cpufreq.h>
+#include <linux/compiler.h>
+#include <linux/dmi.h>
+#include <linux/delay.h>
+#include "linux/completion.h"
+
+#include <asm/processor.h>
+#include "asm/pll.h"
+#include "asm/pll_if.h"
+#include "asm/time.h"
+
+#define cf750gxmChangingPll (0x80000000)
+#define cf750gxmChangingPllBit (31)
+#define cf750gxmTurningIdlePllOff (0x40000000)
+#define cf750gxmTurningIdlePllOffBit (30)
+
+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) */
+};
+
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
+ "ppc-750gx-cpufreq", msg)
+
+MODULE_AUTHOR("Kevin Diggs");
+MODULE_DESCRIPTION("750GX Dual PLL cpufreq driver");
+MODULE_LICENSE("GPL");
+
+struct cf750gx_t_call_data {
+ struct cpufreq_freqs freqs;
+ unsigned long current_pll;
+ int idle_pll_off;
+};
+
+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,
+};
+
+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;
+
+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;
+
+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;
+
+ cd = (struct cf750gx_t_call_data *)data;
+
+ dprintk("%s(): cd->idle_pll_off is %d, new PLL is 0x%x"
+ ", Switched to %d KHz\n", __func__, cd->idle_pll_off,
+ cd->current_pll, cd->freqs.new);
+
+ cpufreq_notify_transition(&cd->freqs, CPUFREQ_POSTCHANGE);
+
+ if (cd->idle_pll_off) {
+ /*
+ * Assemble call to turn idle PLL off. Use the value of
+ * current_pll in the call data thing.
+ */
+ idle_pll = get_next_PLL(cd->current_pll)^0x1;
+
+ pll_off_cmd = pllif_pack_state(0, 2);
+
+ /*
+ * Put in correct spot
+ */
+ if (idle_pll) {
+ /*
+ * Use PLL 1
+ */
+ new_pll = ((PLL1_DO_CFG|PLL1_DO_RNG)<<24);
+ } else {
+ /*
+ * Since active PLL is 1, switch off PLL 0
+ */
+ pll_off_cmd = pll_off_cmd<<(PLL0_CFG_SHIFT-
+ PLL1_CFG_SHIFT);
+
+ new_pll = (PLL0_DO_CFG|PLL0_DO_RNG)<<24;
+ }
+
+ new_pll = new_pll|pll_off_cmd;
+
+ set_bit(cf750gxmTurningIdlePllOffBit, &cf750gx_v_state_bits);
+
+ (void) pllif_modify_PLL(new_pll, 0);
+ } else {
+ clear_bit(cf750gxmChangingPllBit, &cf750gx_v_state_bits);
+
+ complete(&cf750gx_v_exit_completion);
+ }
+
+ return NOTIFY_OK;
+}
+
+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;
+
+ dprintk("%s(): cd->idle_pll_off is %d, new PLL is 0x%x"
+ ", Switched to %d KHz\n", __func__, cd->idle_pll_off,
+ cd->current_pll, cd->freqs.new);
+
+ if (cd->idle_pll_off) {
+ /*
+ * We want to turn off the inactive (idle) pll. Use the state
+ * bits global to figure out whether this call back is for the
+ * original, cpufreq ordered frequency change or our turning
+ * off of the idle PLL.
+ */
+ if (test_bit(cf750gxmTurningIdlePllOffBit,
+ &cf750gx_v_state_bits)) {
+ clear_bit(cf750gxmTurningIdlePllOffBit,
+ &cf750gx_v_state_bits);
+ clear_bit(cf750gxmChangingPllBit,
+ &cf750gx_v_state_bits);
+ complete(&cf750gx_v_exit_completion);
+ }
+ }
+
+ return NOTIFY_OK;
+}
+
+static inline const char *cf750gx_i_relation_str(unsigned int relation)
+{
+ switch (relation) {
+ case CPUFREQ_RELATION_L: return "CPUFREQ_RELATION_L";
+ case CPUFREQ_RELATION_H: return "CPUFREQ_RELATION_H";
+ default: return "relation ??";
+ }
+}
+
+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;
+unsigned int pll;
+unsigned int new_pll;
+unsigned int active_pll;
+struct cpufreq_freqs freqs;
+struct cpufreq_frequency_table *ft = cf750gx_v_f_table;
+
+ 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;
+
+ next_perf_state = ft[next_index].index;
+ next_freq = ft[next_index].frequency;
+ dprintk(__FILE__">%s()-%d: %d KHz (state=%x) selected from table "
+ "(%s)\n", __func__, __LINE__, next_freq, next_perf_state,
+ cf750gx_i_relation_str(relation));
+
+ pll = get_PLL();
+ active_pll = get_active_PLL(pll);
+
+#if 0
+#ifdef CONFIG_HOTPLUG_CPU
+ /* cpufreq holds the hotplug lock, so we are safe from here on */
+ cpus_and(online_policy_cpus, cpu_online_map, policy->cpus);
+#else
+ online_policy_cpus = policy->cpus;
+#endif
+#endif
+
+ if (pllif_pack_state(get_PLL_ratio(active_pll, pll), get_PLL_range(
+ active_pll, pll)) == next_perf_state) {
+ dprintk("Already at target state (%x)\n",
+ next_perf_state);
+ goto cf750gxTargetFreqSet;
+ }
+
+/* cpus_clear(cmd.mask); */
+
+ dprintk(__FILE__">%s()-%d: Current PLL: 0x%x\n", __func__, __LINE__,
+ pll);
+
+ if (active_pll) {
+ unsigned int current_state;
+
+ /*
+ * Since active PLL is 1, modify PLL 0. First see if it is
+ * already where we want it.
+ */
+ current_state = pllif_pack_state(get_PLL_ratio(0, pll),
+ get_PLL_range(0, pll));
+
+ dprintk(__FILE__">%s()-%d: pll 0 current state: 0x%x\n",
+ __func__, __LINE__, current_state);
+
+ if (current_state == next_perf_state) {
+ new_pll = PLL_DO_SEL<<24;
+ next_perf_state = 0;
+ } else {
+ next_perf_state = next_perf_state<<(PLL0_CFG_SHIFT-
+ PLL1_CFG_SHIFT);
+
+ new_pll = (PLL0_DO_CFG|PLL0_DO_RNG|PLL_DO_SEL)<<24;
+ }
+ } else {
+ unsigned int current_state;
+
+ /*
+ * Use PLL 1. Again, see if it is already at the desired
+ * configuration.
+ */
+ current_state = pllif_pack_state(get_PLL_ratio(1, pll),
+ get_PLL_range(1, pll));
+
+ dprintk(__FILE__">%s()-%d: pll 1 current state: 0x%x\n",
+ __func__, __LINE__, current_state);
+
+ if (current_state == next_perf_state) {
+ new_pll = (PLL_DO_SEL<<24)|PLL_SEL_MASK;
+ next_perf_state = 0;
+ } else {
+ new_pll = ((PLL1_DO_CFG|PLL1_DO_RNG|PLL_DO_SEL)<<24)|
+ PLL_SEL_MASK;
+ }
+ }
+
+ new_pll = new_pll|next_perf_state;
+
+ dprintk(__FILE__">%s()-%d: Modifying PLL: 0x%x\n", __func__, __LINE__,
+ new_pll);
+
+ freqs.old = pllif_cfg_to_freq(get_PLL_ratio(active_pll, pll))/1000;
+ freqs.new = ft[next_index].frequency;
+ freqs.cpu = 0;
+
+ cf750gx_v_switch_call_data.freqs = freqs;
+ cf750gx_v_switch_call_data.current_pll = new_pll;
+ cf750gx_v_switch_call_data.idle_pll_off = cf750gx_v_idle_pll_off;
+
+ cf750gx_v_lock_call_data.freqs = freqs;
+ cf750gx_v_lock_call_data.current_pll = new_pll;
+ cf750gx_v_lock_call_data.idle_pll_off = cf750gx_v_switch_call_data.
+ idle_pll_off;
+
+ dprintk(__FILE__">%s()-%d: freqs.old=%d, freqs.new=%d\n", __func__,
+ __LINE__, freqs.old, freqs.new);
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+ result = pllif_modify_PLL(new_pll, 0);
+
+ if (result < 0)
+ goto cf750gxTargetUnlock;
+
+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;
+}
+
+static int cf750gx_verify(struct cpufreq_policy *policy)
+{
+ dprintk("%s()\n", __func__);
+
+ return cpufreq_frequency_table_verify(policy, cf750gx_v_f_table);
+}
+
+static int cf750gx_cpu_init(struct cpufreq_policy *policy)
+{
+unsigned int pll;
+unsigned int result = 0;
+
+ dprintk("%s()\n", __func__);
+
+ dprintk("policy->policy is %s (%d)\n", (policy->policy ==
+ CPUFREQ_POLICY_POWERSAVE || policy->policy ==
+ CPUFREQ_POLICY_PERFORMANCE)?(policy->policy ==
+ CPUFREQ_POLICY_POWERSAVE?"powersave":"performance"):
+ "undefined", policy->policy);
+
+ pll = get_PLL();
+
+ policy->cur = pllif_cfg_to_freq(get_PLL_ratio(get_active_PLL(pll),
+ pll))/1000;
+
+/* policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; */
+ policy->cpuinfo.transition_latency = pllif_get_latency();
+
+ 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;
+ cf750gx_v_pll_lock_nb.priority = 0;
+
+ result = pllif_register_pll_lock_cb(&cf750gx_v_pll_lock_nb);
+
+ return result;
+
+err_freqfree:
+ return result;
+}
+
+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);
+
+ cpufreq_frequency_table_put_attr(policy->cpu);
+
+ pllif_unregister_pll_switch_cb(&cf750gx_v_pll_switch_nb);
+
+ pllif_unregister_pll_lock_cb(&cf750gx_v_pll_lock_nb);
+
+ cf750gx_v_pll_switch_nb.notifier_call = NULL;
+ cf750gx_v_pll_switch_nb.next = NULL;
+
+ cf750gx_v_pll_lock_nb.notifier_call = NULL;
+ cf750gx_v_pll_lock_nb.next = NULL;
+
+ return 0;
+}
+
+static int cf750gx_resume(struct cpufreq_policy *policy)
+{
+ return 0;
+}
+
+/*
+ * cf750gx_i_show_idle_pll_off - Show state of idle pll off boolean
+ */
+static ssize_t cf750gx_i_show_idle_pll_off(struct cpufreq_policy *policy,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", cf750gx_v_idle_pll_off);
+}
+
+static ssize_t cf750gx_i_store_pll_off(struct cpufreq_policy *policy,
+ const char *buf, size_t count)
+{
+ if (buf[0] == '0')
+ cf750gx_v_idle_pll_off = 0;
+ else if (buf[0] == '1')
+ cf750gx_v_idle_pll_off = !0;
+
+ return 1;
+}
+
+static struct freq_attr idle_pll_off = {
+ .attr = { .name = "idle_pll_off",
+ .mode = 0644,
+ },
+ .show = cf750gx_i_show_idle_pll_off,
+ .store = cf750gx_i_store_pll_off,
+};
+
+/*
+ * cf750gx_i_show_min_max_mode - Show state of min max mode boolean
+ */
+static ssize_t cf750gx_i_show_min_max_mode(struct cpufreq_policy *policy,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", cf750gx_v_min_max_mode);
+}
+
+static ssize_t cf750gx_i_store_min_max_mode(struct cpufreq_policy *policy,
+ const char *buf, size_t count)
+{
+int result;
+
+ if (buf[0] == '0') {
+ cf750gx_v_min_max_mode = 0;
+
+ cf750gx_v_f_table = cf750gx_v_freq_table;
+
+ cpufreq_frequency_table_put_attr(policy->cpu);
+
+ cpufreq_frequency_table_get_attr(cf750gx_v_freq_table,
+ policy->cpu);
+
+ result = cpufreq_frequency_table_cpuinfo(policy,
+ cf750gx_v_freq_table);
+ } else if (buf[0] == '1') {
+ cf750gx_v_min_max_mode = !0;
+
+ cf750gx_v_f_table = cf750gx_v_min_max_freq_table;
+
+ cpufreq_frequency_table_put_attr(policy->cpu);
+
+ cpufreq_frequency_table_get_attr(cf750gx_v_min_max_freq_table,
+ policy->cpu);
+
+ result = cpufreq_frequency_table_cpuinfo(policy,
+ cf750gx_v_min_max_freq_table);
+ }
+
+ return 1;
+}
+
+static struct freq_attr min_max_mode = {
+ .attr = { .name = "min_max_mode",
+ .mode = 0644,
+ },
+ .show = cf750gx_i_show_min_max_mode,
+ .store = cf750gx_i_store_min_max_mode,
+};
+
+
+static struct freq_attr *cf750gxvAttr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ &idle_pll_off,
+ &min_max_mode,
+ NULL,
+};
+
+static struct cpufreq_driver cf750gx_v_drv = {
+ .verify = cf750gx_verify,
+ .target = cf750gx_target,
+ .init = cf750gx_cpu_init,
+ .exit = cf750gx_cpu_exit,
+ .resume = cf750gx_resume,
+ .name = "ppc750gx-cpufreq",
+ .owner = THIS_MODULE,
+ .attr = cf750gxvAttr,
+};
+
+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;
+
+ dprintk("%s()\n", __func__);
+
+ if (!cpu_has_feature(CPU_FTR_DUAL_PLL_750FX))
+ return 0;
+
+ /*
+ * Get processor min and max core frequencies. If they have not been
+ * overriden, then get them from version defaults.
+ */
+ if ((cur_cpu_spec->pvr_value>>16) == 0x7000)
+ pll_defaults = &pll_750fx;
+ else
+ pll_defaults = &pll_750gx;
+
+ cf750gx_v_min_core = override_min_core?override_min_core:pll_defaults->
+ min_core;
+ cf750gx_v_max_core = override_max_core?override_max_core:pll_defaults->
+ max_core;
+
+ dprintk(__FILE__">%s()-%d: cf750gx_v_min_core is %u, "
+ "cf750gx_v_max_core is %u\n", __func__, __LINE__,
+ cf750gx_v_min_core, cf750gx_v_max_core);
+ dprintk(__FILE__">%s()-%d: pll_defaults: min_ratio %d, max_ratio "
+ "%d\n", __func__, __LINE__, pll_defaults->min_ratio,
+ pll_defaults->max_ratio);
+
+ if (!cf750gx_v_min_core) {
+ dprintk("Can't determine minimum core frequency\n");
+ ret = -EINVAL;
+ goto ErrSimple;
+ }
+
+ if (!cf750gx_v_max_core) {
+ dprintk("Can't determine maximum core frequency\n");
+ ret = -EINVAL;
+ goto ErrSimple;
+ }
+
+ bus_clock = pllif_get_bus_clock()/1000;
+
+ /*
+ * Build maximum freq table. This will depend on the bus freq, the core
+ * frequency limits, and the ratios.
+ */
+ min_ratio = pll_defaults->min_ratio;
+ freq = min_ratio*bus_clock;
+
+ if (freq < cf750gx_v_min_core) {
+ /*
+ * Core min is above min ratio and bus speed clock. Find min
+ * ratio such that min ratio * bus speed >= core min.
+ */
+ min_ratio = cf750gx_v_min_core/bus_clock;
+ j = cf750gx_v_min_core%bus_clock;
+
+ if (j)
+ min_ratio++;
+ } else {
+ /*
+ * Core min is below min ratio and speed clock. Reset core min.
+ */
+ cf750gx_v_min_core = freq;
+ }
+
+ max_ratio = pll_defaults->max_ratio;
+ freq = max_ratio*bus_clock;
+
+ if (freq > cf750gx_v_max_core) {
+ /*
+ * Core max is below max ratio and bus speed. Find max ratio
+ * such that max ratio * bus speed <= core max.
+ */
+ max_ratio = cf750gx_v_max_core/bus_clock;
+ } else {
+ /*
+ * Core max is above max ratio and bus speed clock. Reset core
+ * max.
+ */
+ cf750gx_v_max_core = freq;
+ }
+
+ dprintk(__FILE__">%s()-%d: min_ratio is %d, max_ratio is %d\n",
+ __func__, __LINE__, min_ratio, max_ratio);
+
+ /*
+ * Bus ratios for the GX range from 2-20 for 19 INTEGER frequencies.
+ * The above checks may have changed this. There are max_ratio -
+ * min_ratio + 1 different frequencies.
+ */
+ j = max_ratio-min_ratio+1;
+ cf750gx_v_freq_table = kmalloc(sizeof(struct cpufreq_frequency_table)*
+ (j+5), GFP_KERNEL);
+ if (cf750gx_v_freq_table == NULL) {
+ ret = -ENOMEM;
+ goto ErrSimple;
+ }
+
+ dprintk(__FILE__">%s()-%d: cf750gx_v_freq_table=%p\n", __func__,
+ __LINE__, cf750gx_v_freq_table);
+
+ /*
+ * Use index of first entry to keep track of the count (one extra
+ * entry)
+ */
+ cf750gx_v_freq_table[0].frequency = CPUFREQ_ENTRY_INVALID;
+ cf750gx_v_freq_table[0].index = j;
+
+ /*
+ * Populate the table
+ */
+ for (tbp = cf750gx_v_freq_table+1, i = min_ratio; i <= max_ratio;
+ tbp++, i++) {
+ tbp->frequency = i*bus_clock;
+
+ /*
+ * I really don't know what the proper range values for 600 MHz
+ * and 900 MHz are? (The processor may also get pretty mad if
+ * you muck these up!)
+ */
+ if (tbp->frequency < 600000)
+ rng = 2;
+ else if (tbp->frequency < 900000)
+ rng = 0;
+ else
+ rng = 1;
+
+ /*
+ * The computation in the first argument converts the bus
+ * ratio value to the PLL configuration value needed for the
+ * given ratio
+ */
+ tbp->index = pllif_pack_state(i > 10?i+10:(i<<1), rng);
+
+ dprintk(__FILE__">%s()-%d: p->index=%x, ratio=%u, rng=%u\n",
+ __func__, __LINE__, tbp->index, i, rng);
+ }
+
+ /*
+ * 2nd extra array member
+ */
+ tbp->frequency = CPUFREQ_TABLE_END;
+
+ /*
+ * The other 3 extra array members are for the min max table.
+ */
+ cf750gx_v_min_max_freq_table = cf750gx_v_freq_table+
+ cf750gx_v_freq_table[0].index + 2;
+
+ cf750gx_v_min_max_freq_table[0] = cf750gx_v_freq_table[1];
+ cf750gx_v_min_max_freq_table[1] = cf750gx_v_freq_table[
+ cf750gx_v_freq_table[0].index];
+ cf750gx_v_min_max_freq_table[2].frequency = CPUFREQ_TABLE_END;
+
+ cf750gx_v_f_table = minmaxmode?cf750gx_v_min_max_freq_table:
+ cf750gx_v_freq_table;
+
+ ret = cpufreq_register_driver(&cf750gx_v_drv);
+
+ dprintk(__FILE__">%s()-%d: return from cpufreq_register_driver() is "
+ "%d\n", __func__, __LINE__, ret);
+
+ if (ret)
+ goto ErrFreqTable;
+
+ErrSimple:
+ return ret;
+ErrFreqTable:
+ kfree(cf750gx_v_freq_table);
+ return ret;
+}
+
+static void __exit cf750gx_exit(void)
+{
+ dprintk("%s()\n", __func__);
+
+ cpufreq_unregister_driver(&cf750gx_v_drv);
+
+ if (cf750gx_v_freq_table)
+ kfree(cf750gx_v_freq_table);
+
+ cf750gx_v_freq_table = cf750gx_v_min_max_freq_table =
+ cf750gx_v_f_table = NULL;
+
+ return;
+}
+
+module_param(override_max_core, uint, 0644);
+MODULE_PARM_DESC(override_max_core,
+ "clock frequency in KHz.");
+
+module_param(override_min_core, uint, 0644);
+MODULE_PARM_DESC(override_min_core,
+ "clock frequency in KHz.");
+
+module_param(minmaxmode, uint, 0644);
+MODULE_PARM_DESC(minmaxmode,
+ "Use only the minimum and maximum frequencies.");
+
+late_initcall(cf750gx_init);
+module_exit(cf750gx_exit);
+
+MODULE_ALIAS("dual-pll");
Index: arch/powerpc/kernel/cpu/Makefile
===================================================================
--- /dev/null 2004-08-10 18:55:00.000000000 -0700
+++ arch/powerpc/kernel/cpu/Makefile 2008-08-14 02:44:30.000000000 -0700
@@ -0,0 +1,6 @@
+#
+# Makefile for ppc CPU details and quirks
+#
+
+obj-$(CONFIG_CPU_FREQ) += cpufreq/
+obj-$(CONFIG_PPC_750GX_DUAL_PLL_IF) += pll_if.o
^ permalink raw reply
* [PATCH 1/4] Add low level PLL config register interface module
From: Kevin Diggs @ 2008-08-25 10:41 UTC (permalink / raw)
To: linuxppc-dev, linux-kernel
This adds a small module to handle the low level details of dealing with the
PLL config register (HID1) found in the IBM 750GX. It provides 2 possible
interfaces, both selectable via kernel config options. One is a sysfs attribute
and the other is a normal function API. It is called pll_if.
The determination of the bus frequency is what worked on a PowerMac 8600. Any
suggestions on a more general solution are welcome.
WARNING - I used some #ifdefs - Let the fur fly!
My name is Kevin Diggs and I approve this patch.
Signed-off-by: Kevin Diggs <kevdig@hypersurf.com>
Index: Documentation/cpu-freq/pll.pl
===================================================================
--- /dev/null 2004-08-10 18:55:00.000000000 -0700
+++ Documentation/cpu-freq/pll.pl 2008-08-15 13:42:09.000000000 -0700
@@ -0,0 +1,762 @@
+#!/usr/bin/perl -w
+
+=head1 NAME
+
+pll.pl - Provide user friendly interface to sysfs attribute for the 750GX PLL.
+This uses the pll_if module.
+
+=head1 SYSNOPSIS
+
+ pll.pl [ -bdhtv ] [ -f { clk | ratio }]
+ b: print bus frequency
+ d: dump current pll configuration
+ h: print simple usage information
+ f: set frequency to specified clk (MHz) or ratio (r suffix)
+ t: toggle selected pll. Use with -f to cause a switch to the newly
+ modified PLL on lock.
+ v: enable verbose
+
+=head1 DESCRIPTION
+
+This utility provides a user friendly interface to the sysfs attribute that is
+provided by the pll_if module to control the PLL configuration register (HID1)
+in the IBM PowerPC 750FX and 750GX processors.
+
+=over 4
+
+=item -b
+
+print the bus frequency which is used along with the ratios to compute the
+processor clock frequency.
+
+=pod
+
+The method used to get the bus frequency is to use the value of the
+clock-frequency property from the root of the OF tree.
+
+=item -d
+
+prints the current value of the PLL configuration register in a human readable
+form (well ... more or less).
+
+=item -h
+
+print a simple help message.
+
+=item -t
+
+Toggles the selected PLL that is driving the cpu clock. When used with -f causes
+a switch to the new PLL upon lock (when the lock timeout expires).
+
+=item -v
+
+Enable verbose mode. Mostly just prints the file paths that stuff is pulled
+from.
+
+=item -f
+
+Sets the INACTIVE PLL to the selected clock frequency in MHz. If the value has
+an "r" suffix, it is taken as a ratio. This also recognizes the special value
+"off" (-foff) to turn off the INACTIVE PLL.
+
+=back
+
+=head1 AUTHOR
+
+kevin diggs
+
+=cut
+
+use warnings;
+use Getopt::Std;
+
+#
+# The layout of the PLL register (HID1) is:
+#
+# 0 4|5 6|7|8| 9 11|12 13|14| 15 |16 20|21 22|23|24 28|29 30| 31
+# PCE |PRE|v|v| Res | Res |v | PS | PC0 | PR0 |v | PC1 | PR1 |Res
+# | | | |
+# PSTAT1 -| | | |
+# ECLK -----| | |
+# PI0 --------------------| |
+# Res ----------------------------------------|
+#
+# PCE PLL0 read-only external config
+# PRE PLL0 read-only external range
+# PSTAT1 PLL status (0 -> PLL0, 1 -> PLL1)
+# ECLK 1 -> enable clkout pin
+# PI0 PLL0 control: 0 -> external
+# PS PLL select: 0 -> PLL0, 1 -> PLL1
+# PC0 PLL0 configuration
+# PR0 PLL0 range
+# PC1 PLL1 configuration
+# PR1 PLL1 range
+#
+# PLL_CFG bus ratio PLL_CFG bus ratio
+# 00000 off 10000 8
+# 00001 off 10001 8.5
+# 00010 bypass 10010 9
+# 00011 bypass 10011 9.5
+# 00100 2 10100 10
+# 00101 2.5 10101 11
+# 00110 3 10110 12
+# 00111 3.5 10111 13
+# 01000 4 11000 14
+# 01001 4.5 11001 15
+# 01010 5 11010 16
+# 01011 5.5 11011 17
+# 01100 6 11100 18
+# 01101 6.5 11101 19
+# 01110 7 11110 20
+# 01111 7.5 11111 off
+#
+# PLL_RNG range
+# 00 600 - 900
+# 01 900 - 1000
+# 10 500 - 600
+#
+# IBM bit numbering:
+# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
+# 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6
+#
+# 26 27 28 29 30 31
+# 5 4 3 2 1 0
+#
+*pllcBusFreqFile=\"/proc/device-tree/clock-frequency";
+#*pllcCPUPVR=\"/proc/device-tree/PowerPC,*/cpu-version";
+*pllcSysFS=\"/sys/devices/system/cpu/cpu0/*pll";
+
+#
+# Update the value of the PLL configuration register based on the crap passed
+# in. The upper 8 bits (0 - 7) are read only and will be used as flags to con-
+# trol what we are doing:
+#
+*pllcPLL0_DO_CFG= \0x80000000; # PLL0 configuration is valid
+*pllcPLL0_DO_RNG= \0x40000000; # PLL0 range is valid
+*pllcPLL1_DO_CFG= \0x20000000; # PLL1 configuration is valid
+*pllcPLL1_DO_RNG= \0x10000000; # PLL1 range is valid
+*pllcPLL_DO_SEL= \0x08000000; # PLL select is valid
+#*pllcPLL0_DO_CONTROL= \0x04000000; # PLL0 control is valid
+
+#*pllcPLL0_CONTROL_MASK= \0x20000;
+*pllcPLL_SEL_MASK= \0x10000;
+#*pllcPLL0_CFG_MASK= \0x0f800;
+*pllcPLL0_CFG_SHIFT= \11;
+#*pllcPLL0_RNG_MASK= \0x00600;
+*pllcPLL0_RNG_SHIFT= \9;
+#*pllcPLL1_CFG_MASK= \0x000f8;
+*pllcPLL1_CFG_SHIFT= \3;
+#*pllcPLL1_RNG_MASK= \0x00006;
+*pllcPLL1_RNG_SHIFT= \1;
+
+sub plliCommaize
+{
+my ($num) = @_;
+
+ 1 while $num =~ s/(\d)(\d\d\d)(?!\d)/$1,$2/;
+
+ return $num;
+}
+
+sub plliGetActivePLL
+{
+my ($pll) = @_;
+
+ #
+ # Put PSTAT1 (bit 7 by IBM numbering) into the LSBit position.
+ #
+ $pll = $pll >> 24;
+ $pll = $pll & 0x1;
+
+ return $pll;
+}
+
+sub plliGetPLLRatio
+{
+my ($ratio,$config) = @_;
+
+ #
+ # Turn ratio into a right shift count. 0 -> 11, 1 -> 3
+ #
+ $ratio = ($ratio & 0x1) ^ 1;
+ $ratio = $ratio << 3;
+ $ratio = $ratio + 3;
+
+ $config = $config >> $ratio;
+ $config = $config & 0x1F;
+
+ return $config;
+}
+
+sub plliGetPLLRange
+{
+my ($range,$config) = @_;
+
+ #
+ # Turn range into a right shift count. 0 -> 9, 1 -> 1
+ #
+ $range = ($range & 0x1) ^ 1;
+ $range = $range << 3;
+ $range = $range + 1;
+
+ $config = $config >> $range;
+ $config = $config & 0x3;
+
+ return $config;
+}
+
+sub plliPLLOff
+{
+my ($pll_ratio) = @_;
+
+ return $pll_ratio==0 || $pll_ratio==1 || $pll_ratio==31 ||
+ $pll_ratio==2 || $pll_ratio==3;
+}
+
+sub plliGetBusFreq
+{
+my ($sfile) = @_;
+
+my $open_status;
+my $byte_count;
+my $bus_freq;
+my $bus_freq_unpacked;
+
+ #
+ # Get bus clock frequency. Get this from the root of the device tree in
+ # /proc in the "clock-frequency" property of the root node.
+ #
+ $byte_count=0;
+
+ $open_status=open FH,"<",$sfile;
+ if(!defined $open_status) {die "Can't open $sfile.\n";}
+
+ binmode FH,":raw";
+ $byte_count=read FH, $bus_freq, 4;
+ close FH;
+
+ #
+ # Convert binary in bus_freq to normal perl value
+ #
+ $bus_freq_unpacked=unpack "N",$bus_freq;
+
+ return $bus_freq_unpacked;
+}
+
+sub plliGetPVR
+{
+my ($sfile) = @_;
+
+my $cpu_pvr;
+my $cpu_pvr_unpacked;
+my $processor_version;
+my @pvr_list;
+my $byte_count;
+my $open_status;
+my @out;
+
+ @out=();
+
+ #
+ # Get processor pvr. It can be found in cpu-version property of the
+ # "PowerPC,*" directory.
+ #
+ $byte_count=0;
+ @pvr_list=();
+ @pvr_list=glob $sfile;
+
+ $open_status=open FH,"<",$pvr_list[0];
+ if(!defined $open_status) {die "Can't open $pvr_list[0].\n";}
+
+ binmode FH,":raw";
+ $byte_count=read FH, $cpu_pvr, 4;
+ close FH;
+
+ #
+ # Convert binary in cpu_pvr to normal perl value
+ #
+ $cpu_pvr_unpacked=unpack "N",$cpu_pvr;
+ $processor_version=unpack "n",$cpu_pvr;
+
+ #
+ # Put pvr in index 0, version in 1, globbed file name in 2.
+ #
+ push @out,unpack "N",$cpu_pvr;
+ push @out,unpack "n",$cpu_pvr;
+ push @out,$pvr_list[0];
+
+ return wantarray ? @out:$out[0];
+}
+
+sub plliGetPLL
+{
+my ($sfile) = @_;
+
+my $byte_count;
+my $open_status;
+my @pll_list;
+my $pll;
+my @out;
+
+ @out=();
+
+ #
+ # Get value of pll. It is in /sys/devices/system/cpu/cpu0/ppc750gxpll
+ #
+ $byte_count=0;
+ @pll_list=();
+ @pll_list=glob $sfile;
+
+ $open_status=open FH,"<",$pll_list[0];
+ if(!defined $open_status) {die "Can't open $pll_list[0].\n";}
+
+ #
+ # Currently, this is ascii.
+ #
+ $pll=<FH>;
+ close FH;
+
+ chop $pll;
+
+ #
+ # Stick pll (in ascii?) in element 0. Put globbed file name in 1.
+ #
+ push @out,$pll;
+ push @out,$pll_list[0];
+
+ return wantarray ? @out:$out[0];
+}
+
+sub plliSetPLL
+{
+my ($sfile,$pll) = @_;
+
+my $byte_count;
+my $open_status;
+my @pll_list;
+
+ #
+ # Set value of pll. It is in /sys/devices/system/cpu/cpu0/ppc750gxpll
+ #
+ $byte_count=0;
+ @pll_list=();
+ @pll_list=glob $sfile;
+
+ $open_status=open FH,">",$pll_list[0];
+ if(!defined $open_status) {die "Can't open $pll_list[0].\n";}
+
+ printf FH "%x",$pll;
+ close FH;
+}
+
+sub plliAsciiizePLL
+{
+my ($pll,$bus_clk,$sfile,$verbose) = @_;
+my @range_string = ("default","high","low","reserved");
+my @fmt_list = ("off","bypass","%d","%d.5");
+#
+# This mess represents a 32 element array of 2 bit values to turn the ratio
+# into one of the above format strings.
+#
+my @ratio_fmt = (0xEEEEEE50,0x2AAAAAEE);
+my $temp0;
+my $i;
+my $j;
+my $rat0_ext;
+my $rng0_ext;
+my $pll0_cfg_ext;
+my $rat0;
+my $rng0;
+my $pll0_cfg;
+my $rat1;
+my $rng1;
+my $pll1_cfg;
+my @out;
+
+ @out=();
+
+ $pll=hex $pll;
+
+ #
+ # PCE, bits [0-4]
+ #
+ $temp0=$pll>>27;
+ $temp0=$temp0&0x1F;
+
+ if($temp0>15)
+ {
+ $i=$temp0-16;
+ $j=$ratio_fmt[1];
+ }
+ else
+ {
+ $i=$temp0;
+ $j=$ratio_fmt[0];
+ }
+
+ $rng0=($j>>($i<<1))&0x3;
+
+ if($temp0>20) {$temp0=($temp0-10)<<1;}
+ $rat0_ext=$temp0;
+
+ $pll0_cfg_ext=sprintf $fmt_list[$rng0],$rat0_ext>>1;
+ #
+ # PRE, bits [5-6]
+ #
+ $rng0_ext=($pll>>25)&0x3;
+ #
+ # PC0, bits [16-20]
+ #
+ $temp0=plliGetPLLRatio(0,$pll);
+
+ if($temp0>15)
+ {
+ $i=$temp0-16;
+ $j=$ratio_fmt[1];
+ }
+ else
+ {
+ $i=$temp0;
+ $j=$ratio_fmt[0];
+ }
+
+ $rng0=($j>>($i<<1))&0x3;
+
+ if($temp0>20) {$temp0=($temp0-10)<<1;}
+ $rat0=$temp0;
+
+ $pll0_cfg=sprintf $fmt_list[$rng0],$rat0>>1;
+ #
+ # PR0, bits [21-22]
+ #
+ $rng0=plliGetPLLRange(0,$pll);
+ #
+ # PC1, bits [24-28]
+ #
+ $temp0=plliGetPLLRatio(1,$pll);
+
+ if($temp0>15)
+ {
+ $i=$temp0-16;
+ $j=$ratio_fmt[1];
+ }
+ else
+ {
+ $i=$temp0;
+ $j=$ratio_fmt[0];
+ }
+
+ $rng1=($j>>($i<<1))&0x3;
+
+ if($temp0>20) {$temp0=($temp0-10)<<1;}
+ $rat1=$temp0;
+
+ $pll1_cfg=sprintf $fmt_list[$rng1],$rat1>>1;
+ #
+ # PR1, bits [29-30]
+ #
+ $rng1=plliGetPLLRange(1,$pll);
+
+ push @out,sprintf "0x%08x: ",$pll;
+ push @out,sprintf "PLL0 external (%s (%s MHz), %s), ",$pll0_cfg_ext,
+ plliCommaize($rat0_ext*$bus_clk/2000000),
+ $range_string[$rng0_ext];
+ push @out,sprintf "PLL %d selected, ",plliGetActivePLL($pll);
+ push @out,sprintf "PLL0 %s, ",($pll>>17)&0x1?"internal":"external";
+ push @out,sprintf "PLL0 (%s (%s MHz), %s), ",$pll0_cfg,
+ plliCommaize($rat0*$bus_clk/2000000),$range_string[$rng0];
+ push @out,sprintf "PLL1 (%s (%s MHz), %s)",$pll1_cfg,
+ plliCommaize($rat1*$bus_clk/2000000),$range_string[$rng1];
+
+ if($verbose)
+ {
+ push @out,sprintf "\n\tFrom file %s.",$sfile;
+ }
+
+# return wantarray ? @out:$out[0];
+ return @out;
+}
+
+sub plliPrintHelp
+{
+ printf "%s: [ -bdhtv ] [ -f { clk | ratio }] [ -p pll ] [ -s pll ]\n",
+ $0;
+ printf "\tb:\tprint bus frequency\n";
+ printf "\td:\tdump current pll configuration\n";
+ printf "\tf:\tset frequency to specified clk (MHz) or ratio (r suffix)";
+ printf "\n";
+ printf "\tp:\tuse specified pll (0 or 1)\n";
+ printf "\ts:\tswitch to selected pll (0 or 1)\n";
+ printf "\tt:\ttoggle selected pll. Use with -f to cause a switch to ".
+ "the newly\n\t\tmodified PLL on lock.\n";
+ printf "\tv:\tenable verbose\n";
+}
+
+sub plliPrintBusFreq
+{
+my ($bus_freq,$sfile,$verbose) = @_;
+
+ if($bus_freq>1000000) {$bus_freq=$bus_freq/1000000;}
+
+ $bus_freq=plliCommaize $bus_freq;
+
+ print "System Bus Frequency is $bus_freq MHz.\n";
+ print "\tFrom $sfile.\n" if $verbose;
+}
+
+sub plliTogglePLL
+{
+my ($sfile,$bus_freq,$verbose) = @_;
+
+my @pll;
+my $pll;
+my $active_pll;
+my $active_ratio;
+my $other_pll;
+my $other_ratio;
+my $set_pll;
+
+ @pll=plliGetPLL($sfile);
+ $pll=hex $pll[0];
+
+ $active_pll=plliGetActivePLL($pll);
+ $active_ratio=plliGetPLLRatio $active_pll,$pll;
+
+ #
+ # Make sure the "other" pll is active
+ #
+ $other_pll=$active_pll^1;
+ $other_ratio=plliGetPLLRatio $other_pll,$pll;
+printf "active %d, other %d\n",$active_ratio,$other_ratio;
+
+ if(plliPLLOff($other_ratio))
+ {
+ printf "\"Other\" PLL %d (%d) is off. Can't switch to it.\n",
+ $other_pll,$other_ratio;
+ }
+ else
+ {
+ $set_pll=$pllcPLL_DO_SEL;
+ if($other_pll==1) {$set_pll=$set_pll|$pllcPLL_SEL_MASK;}
+
+
+ if($verbose)
+ {
+ if($active_ratio>20) {$active_ratio=($active_ratio-10)
+ <<1;}
+ if($other_ratio>20) {$other_ratio=($other_ratio-10)
+ <<1;}
+ printf "Switching from PLL %d (%d: %s MHz) to PLL %d",
+ $active_pll,$active_ratio,plliCommaize(
+ $active_ratio*$bus_freq/2000000),$other_pll;
+ printf " (%d: %s MHz)\n",$other_ratio,plliCommaize(
+ $other_ratio*$bus_freq/2000000);
+ }
+
+ plliSetPLL $sfile,$set_pll;
+ }
+}
+
+sub plliSetFreq
+{
+my ($sfile,$freq_arg,$pll_num,$pll_set,$bus_freq,$verbose) = @_;
+
+my @pll;
+my $pll;
+my $active_pll;
+my $active_ratio;
+my $set_pll;
+my $new_rat;
+my $new_cfg;
+my $new_rng;
+my $temp;
+my $computed_freq;
+my $cfg_shift;
+my $rng_shift;
+my $freq_copy;
+
+ @pll=plliGetPLL($sfile);
+ $pll=hex $pll[0];
+ $freq_copy=$freq_arg;
+
+ $active_pll=plliGetActivePLL($pll);
+ $active_ratio=plliGetPLLRatio $active_pll,$pll;
+
+ #
+ # Figure out which PLL to use. If the number is passed in via $pll_num,
+ # make sure it is not the active PLL.
+ #
+ if(defined $pll_num)
+ {
+ if($pll_num!=0 && $pll_num!=1)
+ {
+ die sprintf "Specified PLL must be 0 or 1 (%d)\n",
+ $pll_num;
+ }
+
+ if($pll_num==$active_pll)
+ {
+ die sprintf "Can't change active PLL (%d)\n",$pll_num;
+ }
+ }
+ else {$pll_num=$active_pll^1;}
+
+ #
+ # Now analyze the frequency argument. There are three possible formats:
+ # i) a straight number is taken as a MHz value
+ # ii) a number followed by an 'r' (or 'R') to be used as a ratio
+ # iii) the string 'off' to turn the PLL off
+ #
+ if($freq_copy eq "off") {$new_rat=0;}
+ elsif($freq_copy=~/([\d]+)[rR]/)
+ {
+ $new_rat=$1;
+
+ #
+ # Check the range of values. Legal values are [2-20]. Have no
+ # idea which are legal for the core at the given bus frequency.
+ #
+ if($new_rat<2 || $new_rat>20)
+ {
+ die sprintf "Specified ratio (%d) is out of range\n",
+ $new_rat;
+ }
+ }
+ elsif($freq_copy=~/([\d]+)/)
+ {
+ $new_rat=$1*1000000;
+
+ #
+ # Convert frequency to ratio by dividing by $bus_freq. Then
+ # make sure the given ratio is within the legal range. (Don't
+ # know if it is valid for the core.)
+ #
+ $temp=$new_rat/$bus_freq;
+ if($temp<2 || $temp>20)
+ {
+ die sprintf "Specified frequency (%d) results in ratio".
+ " (%d) that is out of range\n",$new_rat,$temp;
+ }
+
+ $new_rat=$temp;
+ }
+ else
+ {
+ die sprintf "Can't parse frequency argument (%s)\n",$freq_arg;
+ }
+
+ #
+ # First convert actual bus ratio to CFG specific value. From 2 to 10
+ # just double it. From 11 to 20 add 10.
+# if($temp0>20) {$temp0=($temp0-10)<<1;}
+ #
+ if($new_rat<11) {$new_cfg=$new_rat<<1;}
+ else {$new_cfg=$new_rat+10;}
+
+ #
+ # Now set the range. I don't know what to do with this? Not sure what
+ # the correct values are for 600,000,000 and 900,000,000 (See table 5-1
+ # in 750GX data sheet).
+ #
+ $computed_freq=$bus_freq*$new_rat;
+ if($computed_freq<600000000) {$new_rng=2;}
+ elsif($computed_freq<900000000) {$new_rng=0;}
+ else {$new_rng=1;}
+
+ if($pll_num==0)
+ {
+ $cfg_shift=$pllcPLL0_CFG_SHIFT;
+ $rng_shift=$pllcPLL0_RNG_SHIFT;
+ $set_pll=$pllcPLL0_DO_CFG|$pllcPLL0_DO_RNG;
+ }
+ else
+ {
+ $cfg_shift=$pllcPLL1_CFG_SHIFT;
+ $rng_shift=$pllcPLL1_RNG_SHIFT;
+ $set_pll=$pllcPLL1_DO_CFG|$pllcPLL1_DO_RNG;
+ }
+
+ $set_pll=$set_pll|($new_cfg<<$cfg_shift)|($new_rng<<$rng_shift);
+
+ #
+ # If the pll_set argument is defined, then also encode a PLL change to
+ # the new PLL
+ #
+ if(defined $pll_set && $freq_arg ne "off")
+ {
+ $set_pll=$set_pll|$pllcPLL_DO_SEL;
+
+ if($pll_num==1) {$set_pll=$set_pll|$pllcPLL_SEL_MASK;}
+ }
+
+ if($verbose)
+ {
+ if($freq_arg eq "off")
+ {
+ printf "Turning PLL %d off\n",$pll_num;
+ }
+ else
+ {
+ printf "Setting PLL %d to %s MHz (%d)\n",$pll_num,
+ plliCommaize($computed_freq/1000000),$new_cfg;
+ }
+
+ if(defined $pll_set && $freq_arg ne "off")
+ {
+ printf "\tAlso switching to the newly modified PLL\n";
+ }
+ }
+
+ plliSetPLL $sfile,$set_pll;
+}
+
+our ($opt_o, $opt_i, $opt_f);
+our %pllgOptHash;
+my $pllgBusFreq;
+my $pllgVerbose;
+my @pvr;
+my @pll;
+
+$pllgVerbose=0;
+
+getopts("bdf:hp:s:tv",\%pllgOptHash);
+
+if(defined $pllgOptHash{h} && $pllgOptHash{h}==1) {plliPrintHelp;}
+
+if(defined $pllgOptHash{v} && $pllgOptHash{v}==1) {$pllgVerbose=1;}
+
+#@pvr=plliGetPVR $pllcCPUPVR;
+#printf "Printed output from plliGetPVR() is %x.\n",plliGetPVR($pllcCPUPVR);
+#printf "CPU PVR from \"%s\" is %8x\n",$pvr[2],$pvr[0];
+#printf "CPU Version is %4x\n",$pvr[1];
+
+$pllgBusFreq=plliGetBusFreq $pllcBusFreqFile;
+
+@pll=plliGetPLL $pllcSysFS;
+
+if(defined $pllgOptHash{d} && $pllgOptHash{d}==1)
+{
+ print plliAsciiizePLL($pll[0],$pllgBusFreq,$pll[1],$pllgVerbose),"\n";
+
+ #
+ # Only do -d once if we aren't changing the PLL
+ #
+ if(!defined $pllgOptHash{t} && !defined $pllgOptHash{f} && !defined
+ $pllgOptHash{s}) {exit;}
+}
+
+if(defined $pllgOptHash{b} && $pllgOptHash{b}==1) {plliPrintBusFreq
+ $pllgBusFreq,$pllcBusFreqFile,$pllgVerbose;}
+
+if(defined $pllgOptHash{t} && $pllgOptHash{t}==1 && !defined $pllgOptHash{f})
+ {plliTogglePLL $pllcSysFS,$pllgBusFreq,$pllgVerbose;}
+
+if(defined $pllgOptHash{f}) {plliSetFreq $pllcSysFS,$pllgOptHash{f},
+ ,$pllgOptHash{p},$pllgOptHash{t},$pllgBusFreq,$pllgVerbose;}
+
+@pll=plliGetPLL $pllcSysFS;
+
+if(defined $pllgOptHash{d} && $pllgOptHash{d}==1) {print plliAsciiizePLL(
+ $pll[0],$pllgBusFreq,$pll[1],$pllgVerbose),"\n";}
+
+exit;
Index: arch/powerpc/kernel/cpu/pll_if.c
===================================================================
--- /dev/null 2004-08-10 18:55:00.000000000 -0700
+++ arch/powerpc/kernel/cpu/pll_if.c 2008-08-25 01:38:30.000000000 -0700
@@ -0,0 +1,800 @@
+/*
+ * pll_if.c - low level interface to HID1 (PLL config) in the PowerPC 750GX
+ *
+ * Copyright (C) 2008 kevin Diggs
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include "linux/init.h"
+#include "linux/module.h"
+#include <linux/autoconf.h>
+#include "linux/kernel.h"
+#include <linux/errno.h>
+#include <linux/cpu.h>
+#include "linux/of.h"
+#include "linux/notifier.h"
+#include "linux/delay.h"
+#include "linux/completion.h"
+
+#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_SYSFS
+#include "linux/sysdev.h"
+#endif
+#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_HRTIMER
+#include "linux/hrtimer.h"
+#endif
+
+#include "asm/time.h"
+#include <asm/mmu.h>
+#include <asm/processor.h>
+#include <asm/pgtable.h>
+#include <asm/cputable.h>
+#include <asm/system.h>
+#include <asm/pll_if.h>
+#include <asm/pll.h>
+#include <asm/smp.h>
+
+MODULE_LICENSE("GPL");
+
+static DECLARE_COMPLETION(pllif_v_exit_completion);
+
+static unsigned int boot_ratio;
+static unsigned int pllif_v_bus_clock;
+
+static unsigned int override_bus_clock = 0;
+
+#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_HRTIMER
+static enum hrtimer_restart pllif_i_timer_f(struct hrtimer *hrt);
+static struct hrtimer pll_timer;
+static unsigned long hrtimers_got_no_freakin_callback_data;
+#ifdef DEBUG
+cycles_t pll_time_stamp;
+#endif
+#else
+static void pllif_i_timer_f(unsigned long newPLL);
+static struct timer_list pll_timer;
+cycles_t pll_time_stamp;
+#endif
+
+#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_SYSFS
+
+unsigned long boot_loops;
+static struct sys_device *sysdev_cpu;
+
+static ssize_t show_ppc750gxpll(struct sys_device *dev, char *buf)
+{
+ return sprintf(buf, "%x\n", get_PLL());
+}
+
+static ssize_t __used store_ppc750gxpll(struct sys_device *dev,
+ const char *buf, size_t count)
+{
+unsigned long pll_ul;
+int ret;
+
+ pr_debug(__FILE__">%s()-%d: buf=%s\n", __func__, __LINE__, buf);
+
+ ret = strict_strtoul(buf, 16, &pll_ul);
+
+ pr_debug(__FILE__">%s()-%d: %lx (%lu)\n", __func__, __LINE__, pll_ul,
+ pll_ul);
+
+ if (!ret)
+ ret = 0;
+ else {
+ ret = strlen(buf);
+
+ /* pllif_modify_PLL((unsigned int)pll_ul,!0); */
+ pllif_modify_PLL((unsigned int)pll_ul, 0);
+ }
+
+ return ret;
+}
+
+static SYSDEV_ATTR(ppc750gxpll, 0600, show_ppc750gxpll, store_ppc750gxpll);
+#endif
+
+#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_CPU_FREQ
+struct pllif_t_call_data {
+ void *data;
+ int scalar;
+};
+
+static struct pllif_t_call_data pllif_v_switch_call_data;
+static struct pllif_t_call_data pllif_v_lock_call_data;
+static RAW_NOTIFIER_HEAD(pllif_v_pll_switch_chain);
+static RAW_NOTIFIER_HEAD(pllif_v_pll_lock_chain);
+#endif
+
+/*
+ * This initializes the code for the PLL control:
+ * boot_ratio is used to scale the loops_per_jiffy value from its boot value
+ * boot_loops is the boot value of loops_per_jiffy and is used to compute new
+ * values
+ */
+static int __init init_PLL(void)
+{
+unsigned int temp;
+#ifdef CONFIG_PPC_OF
+const u32 *clk;
+struct device_node *tree_root;
+#endif
+
+ if (!cpu_has_feature(CPU_FTR_DUAL_PLL_750FX))
+ return -ENODEV;
+
+ boot_ratio = 0;
+
+ /*
+ * See if bus clock override was specified
+ */
+ if (override_bus_clock)
+ pllif_v_bus_clock = override_bus_clock*1000;
+
+#ifdef CONFIG_PPC_OF
+ /*
+ * If bus clock is not specified, try to get it via OF
+ */
+ if (!pllif_v_bus_clock) {
+ /*
+ * Get root node (aka MacRISC bus)
+ */
+ tree_root = of_find_node_by_name(NULL, "");
+
+
+ if (tree_root) {
+ clk = of_get_property(tree_root, "clock-frequency",
+ NULL);
+
+ if (clk && *clk)
+ pllif_v_bus_clock = (unsigned int) *clk;
+
+ of_node_put(tree_root);
+
+ pr_debug(__FILE__">%s()-%d: Bus clock from OF is %u\n",
+ __func__, __LINE__, pllif_v_bus_clock);
+ }
+ }
+#endif /* CONFIG_PPC_OF */
+
+ if (!pllif_v_bus_clock) {
+ pr_err(__FILE__">%s()-%d: Can't determine bus clock.\n",
+ __func__, __LINE__);
+
+ return -EINVAL;
+ }
+
+ /*
+ * Make sure the clock frequency is correct
+ */
+ temp = get_PLL();
+ temp = get_PLL_ratio(get_active_PLL(temp), temp);
+
+ ppc_proc_freq = pllif_cfg_to_freq(temp);
+
+#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_SYSFS
+ /*
+ * Units for boot ratio is halves, i.e. 20 is a ratio of 10.
+ * From 21 on the returned value needs to be converted to halves.
+ */
+ if (temp > 20)
+ temp = (temp-10)<<1;
+
+ boot_ratio = temp;
+ boot_loops = loops_per_jiffy;
+
+ /*
+ * Try to get the cpu sysdev
+ */
+ sysdev_cpu = get_cpu_sysdev(boot_cpuid);
+
+ if (sysdev_cpu != NULL)
+ sysdev_create_file(sysdev_cpu, &attr_ppc750gxpll);
+#endif
+
+#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_HRTIMER
+ hrtimer_init(&pll_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+#else
+ init_timer(&pll_timer);
+#endif
+
+ pll_timer.function = pllif_i_timer_f;
+
+ return 0;
+}
+
+/*__initcall(init_PLL); */
+
+static void exit_PLL(void)
+{
+#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_SYSFS
+ if (sysdev_cpu != NULL)
+ sysdev_remove_file(sysdev_cpu, &attr_ppc750gxpll);
+#endif
+
+ /*
+ * Make sure there are no timers pending by making sure we are not
+ * doing anything
+ */
+ wait_for_completion(&pllif_v_exit_completion);
+}
+
+module_init(init_PLL);
+module_exit(exit_PLL);
+
+#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_SYSFS
+static unsigned long pllif_i_new_LPJ(unsigned int oldRatio, unsigned int
+ newRatio, unsigned long LPJ)
+{
+ if (LPJ > 200000000)
+ return LPJ/oldRatio*newRatio;
+ else
+ return LPJ*newRatio/oldRatio;
+}
+
+static inline void pllif_i_update_LPJ(unsigned int oldRatio, unsigned int
+ newRatio, unsigned long LPJ)
+{
+ loops_per_jiffy = pllif_i_new_LPJ(oldRatio, newRatio, LPJ);
+}
+#else
+#define pllif_i_update_LPJ(a, b, c)
+#endif
+
+static void pllif_i_switch_PLLs(unsigned int newPLL)
+{
+#if 0
+unsigned long flags;
+#endif
+unsigned int new_ratio, new_ratio_cp, old_ratio, current_pll, masked_boot_ratio;
+
+ pr_debug(__FILE__">%s()-%d: newPLL=0x%08x\n", __func__, __LINE__,
+ newPLL);
+
+ /*
+ * Compute new loops_per_jiffy
+ */
+ current_pll = get_PLL();
+ new_ratio = get_PLL_ratio(get_next_PLL(newPLL), current_pll);
+ old_ratio = get_PLL_ratio(get_active_PLL(current_pll), current_pll);
+ masked_boot_ratio = boot_ratio&0xff;
+ new_ratio_cp = new_ratio;
+
+ pr_debug(__FILE__">%s()-%d: current_pll=0x%08x, new=%d, old=%d\n",
+ __func__, __LINE__, current_pll, new_ratio, old_ratio);
+
+ current_pll = (current_pll&~PLL_SEL_MASK)|(newPLL&PLL_SEL_MASK);
+
+ pr_debug(__FILE__">%s()-%d: current_pll=0x%08x, new=%d, old=%d\n",
+ __func__, __LINE__, current_pll, new_ratio, old_ratio);
+
+ /*
+ * Convert to halves
+ */
+ if (new_ratio > 20)
+ new_ratio = (new_ratio-10)<<1;
+ if (old_ratio > 20)
+ old_ratio = (old_ratio-10)<<1;
+
+ /*
+ * Make sure that we never shorten the sleep values
+ */
+ if (new_ratio > old_ratio) {
+ if (newPLL&PLL_DO_LPJ)
+ pllif_i_update_LPJ(masked_boot_ratio, new_ratio,
+ boot_loops);
+
+ pr_debug(__FILE__">%s()-%d: masked_boot_ratio=%d, new_ratio="
+ "%d, boot_loops=%ld, loops_per_jiffy=%ld\n", __func__, __LINE__,
+ masked_boot_ratio, new_ratio, boot_loops, loops_per_jiffy);
+
+ set_PLL(current_pll);
+ } else {
+ pr_debug(__FILE__">%s()-%d: masked_boot_ratio=%d, new_"
+ "ratio=%d, boot_loops=%ld, loops_per_jiffy=%ld\n",
+ __func__, __LINE__, masked_boot_ratio, new_ratio,
+ boot_loops, loops_per_jiffy);
+
+ set_PLL(current_pll);
+
+ if (newPLL&PLL_DO_LPJ)
+ pllif_i_update_LPJ(masked_boot_ratio, new_ratio,
+ boot_loops);
+
+ pr_debug(__FILE__">%s()-%d: masked_boot_ratio=%d, new_"
+ "ratio=%d, boot_loops=%ld, loops_per_jiffy=%ld\n",
+ __func__, __LINE__, masked_boot_ratio, new_ratio,
+ boot_loops, loops_per_jiffy);
+ }
+
+ raw_notifier_call_chain(&pllif_v_pll_switch_chain, pllifmPllSwitch,
+ pllif_v_switch_call_data.data);
+
+ /*
+ * This is used to print the clock frequency in /proc/cpuinfo
+ */
+ ppc_proc_freq = pllif_cfg_to_freq(new_ratio_cp);
+ pr_debug(__FILE__">%s()-%d: pllif_cfg_to_freq(%u)=%lu\n", __func__,
+ __LINE__, new_ratio_cp, ppc_proc_freq);
+
+#if 0
+ save_flags(flags);
+ cli();
+
+ loops_per_jiffy = pllif_i_new_LPJ(masked_boot_ratio, new_ratio,
+ boot_loops);
+
+ set_PLL(current_pll);
+
+ restore_flags(flags);
+#endif
+}
+
+#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_HRTIMER
+static enum hrtimer_restart pllif_i_timer_f(struct hrtimer *hrt)
+{
+#ifdef DEBUG
+cycles_t now;
+cycles_t usec, tmp, cntlz, cnttz;
+
+ now = get_cycles();
+
+ now = now-pll_time_stamp;
+
+ /*
+ * Aw cmon, I'm just havin' a little fun with PPC assembly.
+ * Just wish I could find a way to use an rlwinm ...
+ */
+ cnttz = tb_ticks_per_sec; /* needed to get the assembly to ...
+ * look right ... ??? */
+ tmp = now*15625;
+
+ asm (
+ "addi %0,%3,-1\n\t"
+ "andc %1,%3,%0\n\t"
+ "cntlzw %1,%1\n\t"
+ "subfic %1,%1,31\n\t"
+ "cntlzw %0,%2\n\t":
+ "=r"(cntlz), "=r"(cnttz):
+ "r"(tmp), "b"(cnttz)
+ );
+
+ /*
+ * 1,000,000 usec per sec and 1,000,000 is 15625<<6
+ */
+ usec = ((tmp<<cntlz)/(tb_ticks_per_sec>>cnttz))<<6;
+ usec = (usec+(1UL<<(cntlz+cnttz-1)))>>(cntlz+cnttz);
+
+ pr_debug(__FILE__">%s()-%d: Time delta is %lu cycles, "
+ "%lu uS (cntlz=%lu, cnttz=%lu)\n", __func__, __LINE__, now,
+ usec, cntlz, cnttz);
+#endif
+ raw_notifier_call_chain(&pllif_v_pll_lock_chain, pllifmPllLock,
+ pllif_v_lock_call_data.data);
+
+ /*
+ * Clear all lock bits
+ */
+ boot_ratio &= ~(PLL_TIMER|PLL0_LOCK|PLL1_LOCK);
+
+ if ((unsigned int) hrtimers_got_no_freakin_callback_data)
+ pllif_i_switch_PLLs((unsigned int)
+ hrtimers_got_no_freakin_callback_data);
+
+ complete(&pllif_v_exit_completion);
+
+ return HRTIMER_NORESTART;
+}
+#else
+static void pllif_i_timer_f(unsigned long newPLL)
+{
+cycles_t now;
+
+ now = get_cycles();
+ now = now-pll_time_stamp;
+
+#ifdef DEBUG
+ {
+ cycles_t usec, tmp, cntlz, cnttz;
+
+ /*
+ * Aw cmon, I'm just havin' a little fun with PPC assembly.
+ * Just wish I could find a way to use an rlwinm ...
+ */
+ cnttz = tb_ticks_per_sec; /* needed to get the assembly to ...
+ * look right ... ??? */
+#define MULFIRST
+#ifdef MULFIRST
+ tmp = now*15625;
+#else
+ tmp = now;
+#endif
+
+ asm (
+ "addi %0,%3,-1\n\t"
+ "andc %1,%3,%0\n\t"
+ "cntlzw %1,%1\n\t"
+ "subfic %1,%1,31\n\t"
+ "cntlzw %0,%2\n\t":
+ "=r"(cntlz), "=r"(cnttz):
+ "r"(tmp), "b"(cnttz)
+ );
+
+ /*
+ * 1,000,000 usec per sec and 1,000,000 is 15625<<6
+ */
+/* usec = (((now<<cntlz)/(tb_ticks_per_sec>>cnttz)*15625)+(1UL<<
+ (cntlz+cnttz-1-6)))>>(cntlz+cnttz-6); */
+#ifdef MULFIRST
+ usec = ((tmp<<cntlz)/(tb_ticks_per_sec>>cnttz))<<6;
+ usec = (usec+(1UL<<(cntlz+cnttz-1)))>>(cntlz+cnttz);
+#else
+ usec = ((tmp<<cntlz)/(tb_ticks_per_sec>>cnttz)*15625)<<6;
+ usec = (usec+(1UL<<(cntlz+cnttz-1)))>>(cntlz+cnttz);
+#endif
+
+ pr_debug(__FILE__">%s()-%d: Time delta is %lu cycles, "
+ "%lu uS (cntlz=%lu, cnttz=%lu)\n", __func__, __LINE__,
+ now, usec, cntlz, cnttz);
+ }
+#endif
+ /*
+ * Make sure it has been at least 100 usec. 100 usec is 100 *
+ * tb_ticks_per_sec / 1,000,000 cycles, so:
+ * if(now<100*tb_ticks_per_sec/1000000
+ * 1,000,000 is 15625<<6, so:
+ * if((now<<6)<100*tb_ticks_per_sec/15625)
+ * 100 is 25<<2, so:
+ * if((now<<4)<25*tb_ticks_per_sec/15625)
+ * 15625 is 3125*5, so:
+ * if((now<<4)*5<25*tb_ticks_per_sec/3125)
+ * obviously 25/3125 -> 1/125:
+ * if((now<<4)*5<tb_ticks_per_sec/125)
+ */
+ if ((now<<4)*5 < tb_ticks_per_sec/125)
+ udelay(100-now*1000000/tb_ticks_per_sec);
+
+ raw_notifier_call_chain(&pllif_v_pll_lock_chain, pllifmPllLock,
+ pllif_v_lock_call_data.data);
+
+ /*
+ * Clear all lock bits
+ */
+ boot_ratio &= ~(PLL_TIMER|PLL0_LOCK|PLL1_LOCK);
+
+ if ((unsigned int)newPLL)
+ pllif_i_switch_PLLs((unsigned int)newPLL);
+
+ complete(&pllif_v_exit_completion);
+}
+#endif
+
+/*
+ * Handle accesses to the pll register. Examples for write:
+ * value CFGx/RNGx/res effect
+ * 0x08010000 switch to PLL1
+ * 0x08000000 switch to PLL0
+ * 0xc000fa00 1111 1/01/0 PLL0 off (CFG/RNG 31/1)
+ * 0xc000f200 1111 0/01/0 PLL0 to 20x (CFG/RNG 30/1)
+ * 0x30000004 0000 0/10/0 PLL1 off (CFG/RNG 0/2)
+ * 0x30000054 0101 0/10/0 PLL1 to 5x (CFG/RNG a/2)
+ */
+/**
+ * modifyPLL: - Takes steps to modify PLL as requested
+ * @pll: Specifies the new value and desired operation to be performed
+ * @scaleLPJ: flag to indicate whether to scale the loops_per_jiffy value
+ *
+ * Based on the value passed in the pll argument, this takes the steps necessary
+ * to change the PLL as requested. The upper 7 or 8 bits of the PLL are read
+ * only. These bit positions in the pll argument are used to specify flags that
+ * indicate the validity of the other fields in the pll argument. See the
+ * pll_if.h header for detail and actual values.
+ */
+int pllif_modify_PLL(unsigned int pll, int scaleLPJ)
+{
+unsigned int current_pll, work_mask, pll_x;
+int rval = 0;
+
+ pr_debug(__FILE__">%s()-%d:\n", __func__, __LINE__);
+ pr_debug(__FILE__">%s()-%d: pll=0x%08x\n", __func__, __LINE__, pll);
+
+ /*
+ * This is not reentrant
+ */
+ if (test_and_set_bit(PLL_LOCK_BIT, (unsigned long *)&boot_ratio)) {
+ pr_debug(__FILE__">%s()-%d: Busy!\n", __func__, __LINE__);
+ return -EAGAIN;
+ }
+
+ /*
+ * Don't allow any changes if a timer is pending
+ */
+ if (test_bit(PLL_TIMER_BIT, (unsigned long *)&boot_ratio))
+ goto checkPLLBusy;
+
+ INIT_COMPLETION(pllif_v_exit_completion);
+
+ current_pll = get_PLL();
+ work_mask = pll>>24;
+
+ /*
+ * Check to see if the currently selected PLL is being modified
+ */
+ pll_x = get_active_PLL(current_pll);
+
+ if ((pll_x == 0 && work_mask&(PLL0_DO_CFG|PLL0_DO_RNG|PLL0_DO_CONTROL))
+ || (pll_x == 1 && work_mask&(PLL1_DO_CFG|PLL1_DO_RNG)))
+ goto checkPLLInVal;
+
+ /*
+ * Can't change to a PLL that is off. Also can't immediately change to
+ * one that is not locked. Catch that supposedly impossible condition.
+ */
+ if (work_mask&PLL_DO_SEL) {
+ int next_ratio;
+ unsigned int which_config;
+
+ pll_x = get_next_PLL(pll);
+
+ /*
+ * Figure out where the next ratio comes from. It will be from
+ * pll if we are changing the next pll and current_pll if not.
+ */
+ which_config = pll_x?((work_mask&PLL1_DO_CFG)?pll:current_pll):
+ ((work_mask&PLL0_DO_CFG)?pll:current_pll);
+ next_ratio = get_PLL_ratio(pll_x, which_config);
+ if (next_ratio < 4 || next_ratio > 30)
+ goto checkPLLInVal;
+
+ pll_x = ((pll_x == 0 && boot_ratio&PLL0_LOCK) || (pll_x == 1 &&
+ boot_ratio&PLL1_LOCK))?1:0;
+
+ }
+ /*
+ * To avoid complications, don't allow both plls to be half ratios
+ */
+ if (work_mask&PLL0_DO_CFG) {
+ int old_ratio1, new_ratio0;
+
+ old_ratio1 = get_PLL_ratio(1, current_pll);
+ new_ratio0 = get_PLL_ratio(0, pll);
+
+ if (old_ratio1 > 4 && old_ratio1 < 20 && new_ratio0 > 4 &&
+ new_ratio0 < 20 && (old_ratio1&0x1) & (new_ratio0&0x1))
+ goto checkPLLInVal;
+ } else if (work_mask&PLL1_DO_CFG) {
+ int old_ratio0, new_ratio1;
+
+ old_ratio0 = get_PLL_ratio(0, current_pll);
+ new_ratio1 = get_PLL_ratio(1, pll);
+
+ if (old_ratio0 > 4 && old_ratio0 < 20 && new_ratio1 > 4 &&
+ new_ratio1 < 20 && (old_ratio0&0x1) & (new_ratio1&0x1))
+ goto checkPLLInVal;
+ }
+
+ /*
+ * Determine if we will need to schedule a timer for a PLL relock. If
+ * any PLL config is being changed then a timer will be needed. Also
+ * need one if changing to a PLL that is not locked, though that should
+ * not happen.
+ */
+ if ((work_mask&(PLL0_DO_CFG|PLL0_DO_RNG|PLL1_DO_CFG|PLL1_DO_RNG|
+ PLL0_DO_CONTROL)) || (work_mask&PLL_DO_SEL && pll_x)) {
+ unsigned int pll_mask, temp;
+
+ pll_mask = 0;
+
+ if (work_mask&PLL0_DO_CFG) {
+ pll_mask |= PLL0_CFG_MASK;
+
+ /*
+ * Flag that PLL0 needs to relock
+ */
+ boot_ratio |= PLL0_LOCK;
+ }
+
+ if (work_mask&PLL0_DO_RNG)
+ pll_mask |= PLL0_RNG_MASK;
+
+ if (work_mask&PLL1_DO_CFG) {
+ pll_mask |= PLL1_CFG_MASK;
+
+ /*
+ * Flag that PLL1 needs to relock
+ */
+ boot_ratio |= PLL1_LOCK;
+ }
+
+ if (work_mask&PLL1_DO_RNG)
+ pll_mask |= PLL1_RNG_MASK;
+
+ temp = (current_pll&~pll_mask)|(pll&pll_mask);
+
+ if (pll_mask)
+ set_PLL(temp);
+
+ /*
+ * Flag that a timer is pending
+ */
+ boot_ratio |= PLL_TIMER;
+
+ /*
+ * Schedule a timer to clear the PLL lock bits (and signal that
+ * it is ok to select the PLL)
+ */
+#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_HRTIMER
+ /*
+ * Oh please, someone tell me I'm just to stupid to know how
+ * to pass this to the timer function!
+ */
+ hrtimers_got_no_freakin_callback_data = (work_mask&PLL_DO_SEL)?
+ (PLL_DO_SEL<<24)|(scaleLPJ?PLL_DO_LPJ:0)|(pll&
+ PLL_SEL_MASK):0;
+
+ pll_timer.expires = ktime_set(0, 100000);
+
+ hrtimer_start(&pll_timer, pll_timer.expires, HRTIMER_MODE_REL);
+#ifdef DEBUG
+ pll_time_stamp = get_cycles();
+#endif
+#else
+ /*
+ * We might want to pass three pieces of data to the timer
+ * i) that we want to switch PLLs (PLL_DO_SEL)
+ * ii) which PLL to switch to (PLL_SEL_MASK)
+ * iii) flag to control whether loops_per_jiffy is updated
+ * (PLL_DO_LPJ)
+ */
+ pll_timer.data = (work_mask&PLL_DO_SEL)?(PLL_DO_SEL<<24)|(
+ scaleLPJ?PLL_DO_LPJ:0)|(pll&PLL_SEL_MASK):0;
+
+ /*
+ * Relock takes 100 us. See how many jiffies will take care of
+ * it.
+ */
+ pll_timer.expires = (100*HZ/1000000);
+ if (pll_timer.expires == 0)
+ pll_timer.expires = 1;
+
+ pll_timer.expires = jiffies+pll_timer.expires;
+ add_timer(&pll_timer);
+
+ pll_time_stamp = get_cycles();
+#endif
+ } else if (work_mask&PLL_DO_SEL) {
+ pllif_i_switch_PLLs(pll|(scaleLPJ?PLL_DO_LPJ:0));
+ complete(&pllif_v_exit_completion);
+ }
+
+checkPLLOut:
+ clear_bit(PLL_LOCK_BIT, (unsigned long *)&boot_ratio);
+
+ return rval;
+checkPLLBusy:
+ rval = -EBUSY;
+ goto checkPLLOut;
+checkPLLInVal:
+ rval = -EINVAL;
+ complete(&pllif_v_exit_completion);
+ goto checkPLLOut;
+}
+EXPORT_SYMBOL(pllif_modify_PLL);
+
+/**
+ * pllif_cFg_to_freq: - Takes a ratio and returns the frequency
+ * @cfg: The PLL ratio field value
+ *
+ * This takes a PLL ratio field value and uses it along with the bus frequency
+ * to compute the processor frequency.
+ */
+unsigned int pllif_cfg_to_freq(unsigned int cfg)
+{
+ return (cfg < 21?cfg>>1:cfg-10)*pllif_v_bus_clock;
+}
+EXPORT_SYMBOL(pllif_cfg_to_freq);
+
+#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_CPU_FREQ
+/**
+ * pllif_get_bus_clock: - Returns the bus frequency
+ *
+ * This returns the determined bus frequency in Hz.
+ */
+unsigned int pllif_get_bus_clock()
+{
+ return pllif_v_bus_clock;
+}
+EXPORT_SYMBOL(pllif_get_bus_clock);
+
+/**
+ * pllif_register_pll_switch_cb: - Registers a pll switch call back
+ * @nb: structure describing the call back to register
+ *
+ * This registers a call back function that will be called when the clock is
+ * switched from one PLL to the other.
+ */
+int pllif_register_pll_switch_cb(struct notifier_block *nb)
+{
+int ret;
+
+ pllif_v_switch_call_data.data = (void *)nb->next;
+ nb->next = NULL;
+
+ pllif_v_switch_call_data.scalar = nb->priority;
+ nb->priority = 0;
+
+ ret = raw_notifier_chain_register(&pllif_v_pll_switch_chain, nb);
+
+ return ret;
+}
+EXPORT_SYMBOL(pllif_register_pll_switch_cb);
+
+/**
+ * pllif_unregister_pll_switch_cb: - Cancels a previously registered call back
+ * @nb: structure describing the call back to cancel
+ *
+ * This cancels a previously registered switch call back
+ */
+void pllif_unregister_pll_switch_cb(struct notifier_block *nb)
+{
+
+ raw_notifier_chain_unregister(&pllif_v_pll_switch_chain, nb);
+}
+EXPORT_SYMBOL(pllif_unregister_pll_switch_cb);
+
+/**
+ * pllif_register_pll_lock_cb: - Registers a pll lock call back
+ * @nb: structure describing the call back to register
+ *
+ * This registers a call back function that will be called when a PLL has
+ * locked to a new frequency.
+ */
+int pllif_register_pll_lock_cb(struct notifier_block *nb)
+{
+int ret;
+
+ pllif_v_lock_call_data.data = (void *)nb->next;
+ nb->next = NULL;
+
+ pllif_v_lock_call_data.scalar = nb->priority;
+ nb->priority = 0;
+
+ ret = raw_notifier_chain_register(&pllif_v_pll_lock_chain, nb);
+
+ return ret;
+}
+EXPORT_SYMBOL(pllif_register_pll_lock_cb);
+
+/**
+ * pllif_unregister_pll_lock_cb: - Cancels a previously registered call back
+ * @nb: structure describing the call back to cancel
+ *
+ * This cancels a previously registered PLL lock call back
+ */
+void pllif_unregister_pll_lock_cb(struct notifier_block *nb)
+{
+
+ raw_notifier_chain_unregister(&pllif_v_pll_lock_chain, nb);
+}
+EXPORT_SYMBOL(pllif_unregister_pll_lock_cb);
+#endif
+
+module_param(override_bus_clock, uint, 0644);
+MODULE_PARM_DESC(override_bus_clock,
+ "Bus clock frequency in KHz used to compute core clock frequency from"
+ " bus ratios.");
Index: include/asm-powerpc/pll.h
===================================================================
--- /dev/null 2004-08-10 18:55:00.000000000 -0700
+++ include/asm-powerpc/pll.h 2008-08-23 02:05:14.000000000 -0700
@@ -0,0 +1,209 @@
+#ifndef __PLL_H
+#define __PLL_H
+/*
+ Dual PLL functions, for 750FX & 750GX
+ Copyright (C) 2005 by Kevin Diggs
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+ Tue, June 14, 2005.
+ - First public release, contributed by Kevin Diggs.
+ ***********
+ ***********
+
+ Author: Kevin Diggs ()
+*/
+
+#include <asm/processor.h>
+
+/*
+ The layout of the PLL register (HID1) is:
+
+ 0 4|5 6|7|8| 9 11|12 13|14| 15 |16 20|21 22|23|24 28|29 30| 31
+ PCE |PRE|v|v| Res | Res |v | PS | PC0 | PR0 |v | PC1 | PR1 |Res
+ | | | |
+ PSTAT1 -| | | |
+ ECLK -----| | |
+ PI0 --------------------| |
+ Res ----------------------------------------|
+
+ PCE PLL0 read-only external config
+ PRE PLL0 read-only external range
+ PSTAT1 PLL status (0 -> PLL0, 1 -> PLL1)
+ ECLK 1 -> enable clkout pin
+ PI0 PLL0 control: 0 -> external
+ PS PLL select: 0 -> PLL0, 1 -> PLL1
+ PC0 PLL0 configuration
+ PR0 PLL0 range
+ PC1 PLL1 configuration
+ PR1 PLL1 range
+
+ PLL_CFG bus ratio PLL_CFG bus ratio
+ 00000 off 10000 8
+ 00001 off 10001 8.5
+ 00010 bypass 10010 9
+ 00011 bypass 10011 9.5
+ 00100 2 10100 10
+ 00101 2.5 10101 11
+ 00110 3 10110 12
+ 00111 3.5 10111 13
+ 01000 4 11000 14
+ 01001 4.5 11001 15
+ 01010 5 11010 16
+ 01011 5.5 11011 17
+ 01100 6 11100 18
+ 01101 6.5 11101 19
+ 01110 7 11110 20
+ 01111 7.5 11111 off
+
+ PLL_RNG range
+ 00 600 - 900
+ 01 900 - 1000
+ 10 500 - 600
+ */
+
+/**
+ * get_PLL: - return current value of PLL register (HID1)
+ *
+ * This returns the current value of the PLL configuration register (HID1).
+ */
+static inline volatile unsigned int get_PLL(void)
+{
+unsigned int ret;
+
+ __asm__ __volatile__ ("mfspr %0,%1":
+ "=r"(ret):
+ "i"(SPRN_HID1)
+ );
+
+ return ret;
+}
+
+/**
+ * get_active_PLL: - Returns the active PLL (0 or 1)
+ * @config: The PLL register value to return the active PLL from
+ *
+ * This returns the value of the PSTAT1 bit (bit 7, IBM numbering) , right
+ * justified, which indicates which of the PLLs is currently clocking the CPU.
+ */
+static inline unsigned int get_active_PLL(unsigned int config)
+{
+unsigned int ret;
+
+ /*
+ * PSTAT1 to LSBit and mask
+ */
+ __asm__ __volatile__ ("rlwinm %0,%0,8,31,31":
+ "=r"(ret):
+ "0"(config)
+ );
+
+ return ret;
+}
+
+/**
+ * get_next_PLL: - Returns the PLL that is to become active
+ * @config: The PLL register value to return the next PLL from
+ *
+ * This returns the value of the PS bit (bit 15, IBM numbering), right
+ * justified, which indicates which of the PLLs is going to be clocking the CPU.
+ */
+static inline unsigned int get_next_PLL(unsigned int config)
+{
+unsigned int ret;
+
+ /*
+ * PS to LSBit and mask
+ */
+ __asm__ __volatile__ ("rlwinm %0,%0,16,31,31":
+ "=r"(ret):
+ "0"(config)
+ );
+
+ return ret;
+}
+
+/**
+ * get_PLL_ratio: - Returns the selected PLL ratio
+ * @ratio: The ratio that is to be returned (0 or 1)
+ * @config: The PLL register value to return the next PLL from
+ *
+ * This returns the value of the selected PLL ratio field (PC0 for 0, PC1 for
+ * 1), right justified. It indicates the frequency of the selected PLL.
+ */
+static inline unsigned int get_PLL_ratio(unsigned int ratio, unsigned int
+ config)
+{
+unsigned int ret;
+
+ /*
+ * Turn r3 (ratio) into a rotate count for the selected ratio.
+ * 0 -> 21, 1 -> 29
+ */
+ __asm__ __volatile__ (
+ "slwi %0,%0,3\n"
+ "addi %0,%0,21\n"
+ "rlwnm %0,%1,%0,27,31\n":
+ "=b"(ret):
+ "r"(config), "0"(ratio)
+ );
+
+ return ret;
+}
+
+/**
+ * get_PLL_range: - Returns the selected PLL range
+ * @range: The range that is to be returned (0 or 1)
+ * @config: The PLL register value to return the next PLL from
+ *
+ * This returns the value of the selected PLL range field (PR0 for 0, PR1 for
+ * 1), right justified.
+ */
+static inline unsigned int get_PLL_range(unsigned int range, unsigned int
+ config)
+{
+unsigned int ret;
+
+ /*
+ * Turn r3 (range) into a rotate count for the selected range.
+ * 0 -> 23, 1 -> 31
+ */
+ __asm__ __volatile__ (
+ "slwi %0,%0,3\n"
+ "addi %0,%0,23\n"
+ "rlwnm %0,%1,%0,30,31\n":
+ "=b"(ret):
+ "r"(config), "0"(range)
+ );
+
+ return ret;
+}
+
+/**
+ * get_PLL: - sets a new value in the PLL register
+ * @config: The new value for the PLL register (HID1)
+ *
+ * This stores a new value in the PLL configuration register. It is possible to
+ * freeze the system by storing certain illegal values.
+ */
+static inline volatile void set_PLL(unsigned int config)
+{
+ __asm__ __volatile__ ("mtspr %1,%0":
+ :
+ "r"(config), "i"(SPRN_HID1)
+ );
+}
+#endif
Index: include/asm-powerpc/pll_if.h
===================================================================
--- /dev/null 2004-08-10 18:55:00.000000000 -0700
+++ include/asm-powerpc/pll_if.h 2008-08-23 02:04:19.000000000 -0700
@@ -0,0 +1,117 @@
+#ifndef __PLL_IF_H
+#define __PLL_IF_H
+/*
+ High Level wrapper functions for Dual PLL in 750FX & 750GX
+ Copyright (C) 2008 by Kevin Diggs
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+ Fri, April 18, 2008.
+ - First public release, contributed by Kevin Diggs.
+ ***********
+ ***********
+
+ Author: Kevin Diggs ()
+*/
+
+/*
+ * Update the value of the PLL configuration register based on the crap passed
+ * in. The upper 8 bits (0 - 7) are read only and will be used as flags to con-
+ * trol what we are doing:
+ * 0x80 PLL0 configuration is valid
+ * 0x40 PLL0 range is valid
+ * 0x20 PLL1 configuration is valid
+ * 0x10 PLL1 range is valid
+ * 0x08 PLL select is valid
+ * 0x04 PLL0 control is valid
+ * 0x02 Update loops_per_jiffy value
+ *
+ * Make sure that sufficient time (100 us) is given for a PLL that is changed
+ * to relock before selecting it.
+ */
+#define PLL0_DO_CFG (0x80)
+#define PLL0_DO_RNG (0x40)
+#define PLL1_DO_CFG (0x20)
+#define PLL1_DO_RNG (0x10)
+#define PLL_DO_SEL (0x08)
+#define PLL0_DO_CONTROL (0x04)
+#define PLL_DO_LPJ (0x02)
+
+#define PLL0_CONTROL_MASK (0x20000)
+#define PLL_SEL_MASK (0x10000)
+#define PLL0_CFG_MASK (0x0f800)
+#define PLL0_CFG_SHIFT (11)
+#define PLL0_RNG_MASK (0x00600)
+#define PLL0_RNG_SHIFT (9)
+#define PLL1_CFG_MASK (0x000f8)
+#define PLL1_CFG_SHIFT (3)
+#define PLL1_RNG_MASK (0x00006)
+#define PLL1_RNG_SHIFT (1)
+
+#define PLL_LOCK (0x80000000) /* Code lock bit */
+#define PLL_LOCK_BIT (31)
+#define PLL_TIMER (0x40000000) /* Timer is scheduled */
+#define PLL_TIMER_BIT (30)
+#define PLL0_LOCK (0x20000000) /* PLL 0 locking */
+#define PLL0_LOCK_BIT (29)
+#define PLL1_LOCK (0x10000000) /* PLL 1 locking */
+#define PLL1_LOCK_BIT (28)
+
+#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_CPU_FREQ
+#define pllifmPllSwitch (0x80000000)
+#define pllifmPllLock (0x40000000)
+
+extern int pllif_modify_PLL(unsigned int newPLL, int scaleLPJ);
+extern unsigned int pllif_get_bus_clock(void);
+extern unsigned int pllif_cfg_to_freq(unsigned int ratio);
+extern int pllif_register_pll_switch_cb(struct notifier_block *nb);
+extern void pllif_unregister_pll_switch_cb(struct notifier_block *nb);
+extern int pllif_register_pll_lock_cb(struct notifier_block *nb);
+extern void pllif_unregister_pll_lock_cb(struct notifier_block *nb);
+
+/**
+ * pllif_get_latency: - Return processor frequency switch latency
+ *
+ * This returns the latency that a processor frequency switch takes. It is in
+ * nano seconds. The value will depend on whether HRTIMERS are being used.
+ */
+static inline int pllif_get_latency(void)
+{
+#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_HRTIMER
+ return 100000;
+#else
+ return 1000000000/HZ;
+#endif
+}
+
+/**
+ * pllif_pack_state: - Returns the arguments packed together
+ * @cfg: The ratio that is to be used
+ * @rng: The range that is to be used
+ *
+ * This takes a ratio and range and packs them together in the right positions
+ * relative to each other for creating a new PLL value. The value is positioned
+ * correctly for PLL 1. To reposition for PLL 0 do a left shift of
+ * (PLL0_CFG_SHIFT - PLL1_CFG_SHIFT).
+ */
+static inline unsigned int pllif_pack_state(unsigned int cfg, unsigned int
+ rng)
+{
+ return (cfg<<PLL1_CFG_SHIFT)|(rng<<PLL1_RNG_SHIFT);
+}
+#endif
+
+#endif
^ permalink raw reply
* Efficient memcpy()/memmove() for G2/G3 cores...
From: David Jander @ 2008-08-25 9:31 UTC (permalink / raw)
To: linuxppc-dev
Hello,
I was wondering if there is a good replacement for GLibc memcpy() functions,
that doesn't have horrendous performance on embedded PowerPC processors (such
as Glibc has).
I did some simple benchmarks with this implementation on our custom MPC5121
based board (Freescale e300 core, something like a PPC603e, G2, without VMX):
...
unsigned long int a,b,c,d;
unsigned long int a1,b1,c1,d1;
...
while (len >= 32)
{
a = plSrc[0];
b = plSrc[1];
c = plSrc[2];
d = plSrc[3];
a1 = plSrc[4];
b1 = plSrc[5];
c1 = plSrc[6];
d1 = plSrc[7];
plSrc += 8;
plDst[0] = a;
plDst[1] = b;
plDst[2] = c;
plDst[3] = d;
plDst[4] = a1;
plDst[5] = b1;
plDst[6] = c1;
plDst[7] = d1;
plDst += 8;
len -= 32;
}
...
And the results are more than telling.... by linking this with LD_PRELOAD,
some programs get an enourmous performance boost.
For example a small test program that copies frames into video memory (just
RAM) improved throughput from 13.2 MiB/s to 69.5 MiB/s.
I have googled for this issue, but most optimized versions of memcpy() and
friends seem to focus on AltiVec/VMX, which this processor does not have.
Now I am certain that most of the G2/G3 users on this list _must_ have a
better solution for this. Any suggestions?
Btw, the tests are done on Ubuntu/PowerPC 7.10, don't know if that matters
though...
Best regards,
--
David Jander
^ permalink raw reply
* [PATCH 0/4] A a cpufreq driver for the IBM PowerPC 750GX
From: Kevin Diggs @ 2008-08-25 10:01 UTC (permalink / raw)
To: linuxppc-dev, linux-kernel
This patch set adds a cpufreq driver for the IBM PowerPC 750GX processor. It
"should" also work for the 750FX. The patches are:
1) Add low level PLL config register interface module
2) Add cpufreq driver for the 750GX
3) Other PowerPC kernel changes necessary to support the above
4) Add kernel doc for the completion feature, fix split-man.pl in
kernel-doc-nano-HOWTO.txt
These changes are against 2.6.26.
^ permalink raw reply
* Re: boot cuImage on an old u-boot
From: David Gibson @ 2008-08-25 9:36 UTC (permalink / raw)
To: Scott Wood; +Cc: linuxppc-dev, Sebastian Siewior
In-Reply-To: <20080821161505.GA15669@ld0162-tx32.am.freescale.net>
On Thu, Aug 21, 2008 at 11:15:05AM -0500, Scott Wood wrote:
> On Wed, Aug 20, 2008 at 05:09:58PM +0200, Sebastian Siewior wrote:
> > I have here a mpc8540ads board and a u-boot 1.0.0. I've build the
> > defconfig for the board and I tried to boot the genarated
> > cuImage.mpc8540ads image. After the bootm command I see just
> >
> > |8540> bootm 1000000
> > |## Booting image at 01000000 ...
> > | Image Name: Linux-2.6.26
> > | Image Type: PowerPC Linux Kernel Image (gzip compressed)
> > | Data Size: 1232711 Bytes = 1.2 MB
> > | Load Address: 00400000
> > | Entry Point: 004005ac
> > | Verifying Checksum ... OK
> > | Uncompressing Kernel Image ... OK
> > |
> >
> > I don't see the device tree fixups printfs, so crash happend quite
> > early. Anyone an idea what I could have done wrong?
>
> Make sure the device tree contains /chosen/linux,stdout-path.
No paths to properties, remember!
Make sure the device tree contains a linux,stdout-path property in
/chosen.
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
^ permalink raw reply
* Re: [Cbe-oss-dev] powerpc/cell/oprofile: fix mutex locking for spu-oprofile
From: Arnd Bergmann @ 2008-08-25 7:55 UTC (permalink / raw)
To: Paul Mackerras
Cc: Robert Richter, linux-kernel, linuxppc-dev, oprofile-list, cel,
cbe-oss-dev
In-Reply-To: <18610.20951.764057.574721@cargo.ozlabs.ibm.com>
On Monday 25 August 2008, Paul Mackerras wrote:
>=20
> > Since rc4 is out now, I understand if you feel more comfortable with
> > putting the patch into -next instead of -merge.
>=20
> Linus has been getting stricter about only putting in fixes for
> regressions and serious bugs (see his recent email to Dave Airlie on
> LKML for instance). =A0I assume that the corruption is just in the data
> that is supplied to userspace and doesn't extend to any kernel data
> structures.
That's right, please queue it for -next then.
> > Note that the second patch is trivial and fixes an oopsable condition
> > of the kernel, so at least that should still go into 2.6.27.
>=20
> OK, I'll cherry-pick that one for my next batch for Linus.
Thanks,
Arnd <><
^ permalink raw reply
* Re: [Cbe-oss-dev] powerpc/cell/oprofile: fix mutex locking for spu-oprofile
From: Paul Mackerras @ 2008-08-25 6:31 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Robert Richter, linux-kernel, linuxppc-dev, oprofile-list, cel,
cbe-oss-dev
In-Reply-To: <200808211014.42683.arnd@arndb.de>
Arnd Bergmann writes:
> The patch does not fix a regression, the spu-oprofile code basically never
> worked. With the current code in Linux, samples in the profile buffer
> can get corrupted because reader and writer to that buffer use different
> locks for accessing it. It took us several iterations to come up with
> a solution that does not introduce other problems and I didn't want to
> push an earlier version that would need more fixups.
>
> Since rc4 is out now, I understand if you feel more comfortable with
> putting the patch into -next instead of -merge.
Linus has been getting stricter about only putting in fixes for
regressions and serious bugs (see his recent email to Dave Airlie on
LKML for instance). I assume that the corruption is just in the data
that is supplied to userspace and doesn't extend to any kernel data
structures. If it does then we have a much stronger argument for
pushing this stuff for 2.6.27.
> Note that the second patch is trivial and fixes an oopsable condition
> of the kernel, so at least that should still go into 2.6.27.
OK, I'll cherry-pick that one for my next batch for Linus.
Paul.
^ permalink raw reply
* Re: Random crashes with 2.6.27-rc3 on PPC
From: Benjamin Herrenschmidt @ 2008-08-24 22:39 UTC (permalink / raw)
To: Andreas Schwab; +Cc: linuxppc-dev, Linus Torvalds, linux-kernel
In-Reply-To: <je7ia6bhdd.fsf@sykes.suse.de>
On Sun, 2008-08-24 at 16:46 +0200, Andreas Schwab wrote:
> Michael Buesch <mb@bu3sch.de> writes:
>
> > The following workaround seems to fix the crashes on powerpc.
> > However, this patch is clearly not what we want for other architectures,
> > as they might need -fno-omit-frame-pointer to function properly.
>
> This has a better chance to be accepted. :-)
Unfortunately, that won't solve the FTRACE problem.
Ben.
> diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
> index 8b5a7d3..f9a2e48 100644
> --- a/lib/Kconfig.debug
> +++ b/lib/Kconfig.debug
> @@ -394,7 +394,7 @@ config LOCKDEP
> bool
> depends on DEBUG_KERNEL && TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT
> select STACKTRACE
> - select FRAME_POINTER if !X86 && !MIPS
> + select FRAME_POINTER if !X86 && !MIPS && !PPC
> select KALLSYMS
> select KALLSYMS_ALL
>
>
> > Index: linux-2.6/kernel/Makefile
> > ===================================================================
> > --- linux-2.6.orig/kernel/Makefile 2008-08-24 11:50:23.000000000 +0200
> > +++ linux-2.6/kernel/Makefile 2008-08-24 12:15:54.000000000 +0200
> > @@ -92,13 +92,13 @@ obj-$(CONFIG_SMP) += sched_cpupri.o
> > ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y)
> > # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
> > # needed for x86 only. Why this used to be enabled for all architectures is beyond
> > # me. I suspect most platforms don't need this, but until we know that for sure
> > # I turn this off for IA-64 only. Andreas Schwab says it's also needed on m68k
> > # to get a correct value for the wait-channel (WCHAN in ps). --davidm
> > -CFLAGS_sched.o := $(PROFILING) -fno-omit-frame-pointer
> > +CFLAGS_sched.o := $(PROFILING)
>
> CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER is already enabled on powerpc.
>
> Andreas.
>
^ permalink raw reply
* Re: Random crashes with 2.6.27-rc3 on PPC
From: Benjamin Herrenschmidt @ 2008-08-24 22:37 UTC (permalink / raw)
To: Michael Buesch; +Cc: linuxppc-dev, Linus Torvalds, linux-kernel
In-Reply-To: <200808241544.12412.mb@bu3sch.de>
> Thanks for your random guess.
> The following workaround seems to fix the crashes on powerpc.
> However, this patch is clearly not what we want for other architectures,
> as they might need -fno-omit-frame-pointer to function properly.
Well, and -pg requires it, even on powerpc, so that won't work for
ftrace. Any chance you can try the workaround that segher proposed
though ?
http://penguinppc.de/~segher/0001-powerpc-Workaround-for-the-ftrace-problem.patch
His workaround only kicks in with CONFIG_FTRACE, that would have to be
fixed of course. Also, I suspect the bits that have -pg in a flag
"remove" section should have also "fno-omit-frame-pointer" in that
remove section too.
> I reproduced the random crashes of kernel and userspace applications
> (without the following patch) on a vanilla 2.6.26 and 2.6.27-rc{1-4}
> kernel. I did _not_ try a 2.6.25 kernel with -fno-omit-frame-pointer, so
> I don't know if it would also crash then.
>
> I'm currently running more tests on a patched 2.6.27-rc4 kernel, but it
> didn't crash, yet. I already did 5 complete kernel tree compilations. It
> should have crashed by now, but it didn't :)
Thanks !
Ben.
^ permalink raw reply
* Re: Why does one "stw" fail with address translation disabled in PPC405EP?
From: Wolfgang Denk @ 2008-08-24 18:55 UTC (permalink / raw)
To: Zhou Rui; +Cc: linuxppc-dev
In-Reply-To: <1219479992.7565.17.camel@localhost>
Dear Zhou Rui,
In message <1219479992.7565.17.camel@localhost> you wrote:
>
> > > I am running a kernel module which will execute a user space
> > >application. The entry point of the application is 0x100000a0. At the
> >
> > That should be the first clue that you are doing it wrong. Don't do
> > stuff like that in modules...
>
> Oh, but our project needs a function like that ...
You should really think about this. Why do you think you need this?
What exactly are you trying to do? [Probably there are better
approaches to solve your problem...]
> It is physical address at this moment. Address translation is disabled
> automatically (MSR[IR, DR] = [0, 0]) because of TLB Miss Exception and
> Instrunction Storage Exception.
Hm.. are you absolutely sure that the 0x100000a0 mentioned above is a
physical address?
> > Do you have enough DRAM to cover that? Some of those boards only come
> > with 32MiB of DRAM.
>
> My board only has 32MB DRAM. Do you mean 32MB is not enough for that?
Well, 0x1000'00A0 is above 256 MB, while you have only 32 MB RAM
which is most probably mapped from 0x0000'0000...0x01FF'FFFF... So
what you claim to be a physical address (and I think your claim is
wrong) is far outside available physical memory.
> The same codes can run well in a PPC440EP (Yosemite Board) which owns
> 256MB DRAM. At the beginning of my work, I thought memory size may be
> the cause of failure. But I did not know how to demonstrate it. So if
> the limitation of 32MB DRAM leads to the failure, are there any methods
> for the codes to solve it?
I think you got lost on the wrong track. Please describe which task
you want to implement, and there might be another, better approach
for it.
Best regards,
Wolfgang Denk
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de
The management question ... is not _whether_ to build a pilot system
and throw it away. You _will_ do that. The only question is whether
to plan in advance to build a throwaway, or to promise to deliver the
throwaway to customers. - Fred Brooks, "The Mythical Man Month"
^ permalink raw reply
* ppc4xx_pci: disable PCI (PnP) via fdt
From: Matthias Fuchs @ 2008-08-24 16:07 UTC (permalink / raw)
To: linuxppc-dev
Hi,
is it possible to disable PCI PnP via the device tree on a 4xx system?
I am working with a 440EPx board that can be system CPU (with PCI hostbridge)
or a PCI adapter board. In the latter setup I want to disable PCI PnP,
preferred by manipulating the fdt by U-Boot. The PCI mode is
detected by U-Boot through a GPIO signal. I do not want to remove the
complete /plb/pci... path of the device tree for adapter mode , because I do
not want the PCI bus to be totally invisible. lspci and friends should still
show devices on the local PCI bus.
The 4xx PCIE fdt handling handles a 'status' and 'device_type' property.
These properties behave not the way I need and also they are only
available for PCIE.
With arch/ppc kernels I did this by returning '1' from
ppc_md.pci_exclude_device when PCI PnP should not be done.
Even though I could do it the same way with arch/powerpc, I hope
that there is a fdt-way.
Any ideas?
BTW, isn't it a good idea to handle the 'status' and 'device_type' property
for PCI in the same way as for PCIE?
Matthias
^ permalink raw reply
* Re: Random crashes with 2.6.27-rc3 on PPC
From: Michael Buesch @ 2008-08-24 15:00 UTC (permalink / raw)
To: Andreas Schwab; +Cc: Linus Torvalds, linux-kernel, linuxppc-dev
In-Reply-To: <je7ia6bhdd.fsf@sykes.suse.de>
On Sunday 24 August 2008 16:46:38 Andreas Schwab wrote:
> Michael Buesch <mb@bu3sch.de> writes:
>
> > The following workaround seems to fix the crashes on powerpc.
> > However, this patch is clearly not what we want for other architectures,
> > as they might need -fno-omit-frame-pointer to function properly.
>
> This has a better chance to be accepted. :-)
>
> diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
> index 8b5a7d3..f9a2e48 100644
> --- a/lib/Kconfig.debug
> +++ b/lib/Kconfig.debug
> @@ -394,7 +394,7 @@ config LOCKDEP
> bool
> depends on DEBUG_KERNEL && TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT
> select STACKTRACE
> - select FRAME_POINTER if !X86 && !MIPS
> + select FRAME_POINTER if !X86 && !MIPS && !PPC
> select KALLSYMS
> select KALLSYMS_ALL
This is not what my patch is doing.
Your patch always forces FRAME_POINTER off. At least as far as lockdep is concerned.
What about other parts of the kernel that enable FRAME_POINTER?
I think this should be fixed in the makefile by substitution of
-fno-omit-frame-pointer on PPC (and probably depending on the compiler
version).
Otherwise, if somebody else decides to do select FRAME_POINTER in some other
code, the bug will reappear.
I'm also not sure if it's desired to always force FRAME_POINTER off.
--
Greetings Michael.
^ permalink raw reply
* Re: Random crashes with 2.6.27-rc3 on PPC
From: Andreas Schwab @ 2008-08-24 14:46 UTC (permalink / raw)
To: Michael Buesch; +Cc: Linus Torvalds, linux-kernel, linuxppc-dev
In-Reply-To: <200808241544.12412.mb@bu3sch.de>
Michael Buesch <mb@bu3sch.de> writes:
> The following workaround seems to fix the crashes on powerpc.
> However, this patch is clearly not what we want for other architectures,
> as they might need -fno-omit-frame-pointer to function properly.
This has a better chance to be accepted. :-)
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 8b5a7d3..f9a2e48 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -394,7 +394,7 @@ config LOCKDEP
bool
depends on DEBUG_KERNEL && TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT
select STACKTRACE
- select FRAME_POINTER if !X86 && !MIPS
+ select FRAME_POINTER if !X86 && !MIPS && !PPC
select KALLSYMS
select KALLSYMS_ALL
> Index: linux-2.6/kernel/Makefile
> ===================================================================
> --- linux-2.6.orig/kernel/Makefile 2008-08-24 11:50:23.000000000 +0200
> +++ linux-2.6/kernel/Makefile 2008-08-24 12:15:54.000000000 +0200
> @@ -92,13 +92,13 @@ obj-$(CONFIG_SMP) += sched_cpupri.o
> ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y)
> # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
> # needed for x86 only. Why this used to be enabled for all architectures is beyond
> # me. I suspect most platforms don't need this, but until we know that for sure
> # I turn this off for IA-64 only. Andreas Schwab says it's also needed on m68k
> # to get a correct value for the wait-channel (WCHAN in ps). --davidm
> -CFLAGS_sched.o := $(PROFILING) -fno-omit-frame-pointer
> +CFLAGS_sched.o := $(PROFILING)
CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER is already enabled on powerpc.
Andreas.
--
Andreas Schwab, SuSE Labs, schwab@suse.de
SuSE Linux Products GmbH, Maxfeldstraße 5, 90409 Nürnberg, Germany
PGP key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5
"And now for something completely different."
^ permalink raw reply related
* Re: Random crashes with 2.6.27-rc3 on PPC
From: Michael Buesch @ 2008-08-24 13:44 UTC (permalink / raw)
To: benh; +Cc: linuxppc-dev, Linus Torvalds, linux-kernel
In-Reply-To: <1219531969.21386.205.camel@pasglop>
On Sunday 24 August 2008, Benjamin Herrenschmidt wrote:
> Random guess:
>
> CONFIG_FRAME_POINTER=y
> CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y
>
> Note sure what those together do, check if you have any file compiled
> with -fno-omit-frame-pointer and if you do, try to change things so
> that you don't ... we found some miscompiles when that is set, exposed
> by FTRACE typically (which you don't have enabled) but possibly by other
> things.
Thanks for your random guess.
The following workaround seems to fix the crashes on powerpc.
However, this patch is clearly not what we want for other architectures,
as they might need -fno-omit-frame-pointer to function properly.
I reproduced the random crashes of kernel and userspace applications
(without the following patch) on a vanilla 2.6.26 and 2.6.27-rc{1-4}
kernel. I did _not_ try a 2.6.25 kernel with -fno-omit-frame-pointer, so
I don't know if it would also crash then.
I'm currently running more tests on a patched 2.6.27-rc4 kernel, but it
didn't crash, yet. I already did 5 complete kernel tree compilations. It
should have crashed by now, but it didn't :)
The compiler is:
gcc (GCC) 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)
Index: linux-2.6/Makefile
===================================================================
--- linux-2.6.orig/Makefile 2008-08-24 11:49:53.000000000 +0200
+++ linux-2.6/Makefile 2008-08-24 12:16:42.000000000 +0200
@@ -523,13 +523,13 @@ endif
# Force gcc to behave correct even for buggy distributions
# Arch Makefiles may override this setting
KBUILD_CFLAGS += $(call cc-option, -fno-stack-protector)
ifdef CONFIG_FRAME_POINTER
-KBUILD_CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls
+KBUILD_CFLAGS += -fno-optimize-sibling-calls
else
KBUILD_CFLAGS += -fomit-frame-pointer
endif
ifdef CONFIG_DEBUG_INFO
KBUILD_CFLAGS += -g
Index: linux-2.6/kernel/Makefile
===================================================================
--- linux-2.6.orig/kernel/Makefile 2008-08-24 11:50:23.000000000 +0200
+++ linux-2.6/kernel/Makefile 2008-08-24 12:15:54.000000000 +0200
@@ -92,13 +92,13 @@ obj-$(CONFIG_SMP) += sched_cpupri.o
ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y)
# According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
# needed for x86 only. Why this used to be enabled for all architectures is beyond
# me. I suspect most platforms don't need this, but until we know that for sure
# I turn this off for IA-64 only. Andreas Schwab says it's also needed on m68k
# to get a correct value for the wait-channel (WCHAN in ps). --davidm
-CFLAGS_sched.o := $(PROFILING) -fno-omit-frame-pointer
+CFLAGS_sched.o := $(PROFILING)
endif
$(obj)/configs.o: $(obj)/config_data.h
# config_data.h contains the same information as ikconfig.h but gzipped.
# Info from config_data can be extracted from /proc/config*
^ permalink raw reply
* Re: checkpatch nits ...
From: David Howells @ 2008-08-24 12:23 UTC (permalink / raw)
To: Kevin Diggs; +Cc: linuxppc-dev
In-Reply-To: <48AF5818.7010802@hypersurf.com>
Kevin Diggs <kevdig@hypersurf.com> wrote:
> The entire block is:
>
> __asm__ __volatile__ (
> "addi %0,%3,-1\n"
> "andc %1,%3,%0\n"
> "cntlzw %1,%1\n"
> "subfic %1,%1,31\n"
> "cntlzw %0,%2\n":
> "=r"(cntlz), "=r"(cnttz):
> "r"(tmp), "b"(cnttz)
> );
As long as this has no side effects, the __volatile__ isn't necessary. If the
only effect is to produce the specified outputs based on the specified inputs,
the code can be moved or eliminated if the outputs aren't used.
David
^ permalink raw reply
* Re: libfreevec benchmarks
From: Konstantinos Margaritis @ 2008-08-24 8:03 UTC (permalink / raw)
To: rsa; +Cc: linuxppc-dev
In-Reply-To: <1219427047.7917.38.camel@localhost>
=CE=A3=CF=84=CE=B9=CF=82 Friday 22 August 2008 20:44:07 =CE=BF/=CE=B7 Ryan =
S. Arnold =CE=AD=CE=B3=CF=81=CE=B1=CF=88=CE=B5:
> Do you have FSF (Free Software Foundation) copyright assignment yet?
Copyright assignment is not the issue, if there was interest in the first=20
place, that would never had deterred me.
> How've you implemented the optimizations?
Scalar for small sizes, AltiVec for larger (>16 bytes, depending on the=20
routine).
> Optimizations for individual architectures should follow the powerpc-cpu
> precedent for providing these routines, e.g.
>
> sysdeps/powerpc/powerpc32/power6/memcpy.S
> sysdeps/powerpc/powerpc64/power6/memcpy.S
That's the idea I got, but so far I understood that only 64-bit PowerPC/POW=
ER=20
cpus are supported, what about 32-bit cpus? libfreevec isn't ported to 64-b=
it=20
yet (though I will finish that soon). Would it be enough to have one dir li=
ke=20
eg:
sysdeps/powerpc/powerpc32/altivec/
or would I have to refer to specific CPU models? eg 74xx? And use Implies f=
or=20
the rest?
> Today, if glibc is configure with --with-cpu=3D970 it will actually
> default to the power optimizations for the string routines, as indicated
> by the sysdeps/powerpc/powerpc[32|64]/970/Implies files. It'd be worth
> verifying that your baseline glibc runs are against existing optimized
> versions of glibc. If they're not then this is a fault of the distro
> you're testing on.
Well, I used Debian Lenny and OpenSuse 11.0 (using glibc 2.7 and glibc2.8=20
resp. If it doesn't work as supposed, these are two popular distros with a=
=20
broken glibc, which I would think it's not very likely.
> I'm not aware of the status of some of the embedded PowerPC processors
> with-regard to powerpc-cpu optimizations.
Would the G4 and 8610 fall under the "embedded" PowerPC category?
> Our research found that for some tasks on some PowerPC processors the
> expense of reserving the floating point pipeline for vector operations
> exceeds the benefit of using vector insns for the task.
Well, I would advise *strongly* against that, except for specific cases, no=
t=20
for OS-wide functions. For example, in a popular 3D application such as=20
Blender (or the Mesa 3D library), a lot of memory copying is done along wit=
h=20
lots of FPU math. If you use the FPU unit for plain memcpy/etc stuff, you=20
essentially forbid the app to use it for the important stuff, ie math, and =
in=20
the end you lose performance. On the other hand, the AltiVec unit remains=20
unused all the time, and it's certainly more capable and more generic than =
the=20
=46PU for most of the stuff -not to mention that inside the same app, the i=
ssue=20
of context switching becomes unimportant.=20
> Generally our optimizations tend to favor data an average of 12 bytes
> with 1000 byte max. We also favor aligned data and use the existing
> implementation as a model as a baseline for where we try to keep
> unaligned data performance from dropping below.
Please, check the graphs of most libfreevec functions for the sizes=20
12-1000bytes. Apart from strlen(), which is the only function that performs=
=20
better overall than libfreevec, most other functions offer the same=20
performance for sizes up to 48/96 bytes, but then performance increases=20
dramatically due to the use of the vector unit.
> This research would be a good candidate for selectively replacing some
> of the existing libm functionality. Do these results hold for all
> permutations of long double support? Do they hold for x86/x86_64 as
> well as PowerPC? I would suggest against a massive patch to libc-alpha
> and would instead recommend selective, individual replacement of
> fundamental routines to start with accompanied by exhaustive profile
> data. You have to show that you're dedicated to maintenance of these
> routines and you can't overwhelm the reviewers with massive patches.
=46or the moment, my focus is on 32-bit floats only, but the algorithm is t=
he=20
same for 64-bit/128-bit floating point numbers even. It will just use more=
=20
terms. And yes, as I said, it doesn't use AltiVec and is totally cross-
platform -just plain C- and very short code even. I tested the code on an=20
Athlon X2 again and I get even better performance than on the PowerPC CPUs.=
=20
=46or some reason, glibc -and freebsd libc for that matter as I did a look=
=20
around- use very complex source trees with no good reason. The implementati=
on=20
of a sinf() for example is no more than 20 C lines.=20
As for commitment, well I've been working on that stuff since 2004 (with a =
~2y=20
break because of other obligations, army, family, baby, etc :), but unless=
=20
IBM/Freescale choose to dump AltiVec altogether, I don't see myself stoppin=
g=20
working on it. To tell you the truth, the promotion of the vector unit by b=
oth=20
companies has been a disappointment in my eyes at least, so I might just as=
=20
well switch platform... But that won't happen yet anyway.
> Any submission to GLIBC is going to require that you and your code
> follow the GLIBC process or it'll probably be ignored. You can engage
> me directly via CC and I can help you understand how to integrate the
> code but I can't give you a free pass or do the work for you.
I never asked that. However, first it's more imporant to me to show that th=
e=20
code is worth including and then *if* it's proven worthy, then we can worry=
=20
about stuff like copyright assignment, etc.=20
> The new libc-help mailing list was also created as a place for people to
> learn the process and get the patches in a state where they're ready to
> be submitted to libc-alpha.
I will take a look, thanks for that info.
Konstantinos Margaritis
Codex
http://www.codex.gr
^ permalink raw reply
* Re: Random crashes with 2.6.27-rc3 on PPC
From: Michael Buesch @ 2008-08-24 7:23 UTC (permalink / raw)
To: benh; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <1219531969.21386.205.camel@pasglop>
On Sunday 24 August 2008 00:52:49 Benjamin Herrenschmidt wrote:
> On Sat, 2008-08-23 at 16:10 +0200, Michael Buesch wrote:
> > I am seeing random kernel and userland application
> > crashes on a Powerbook running a 2.6.27-rc3 based kernel (wireless-testing.git).
> >
> > The crashes did recently appear. It might be the case that they were
> > introduced with the merge of 2.6.27-rc1 into wireless-testing.
> > I'm not sure on that one, however. Just a guess. I still need to
> > do more testing (also on vanilla upstream kernels).
> >
> > The crashes are completely random and they look like bad hardware.
> > However I cannot reproduce on 2.6.25.9 (That's a kernel I still had
> > installed, so I tried that one). So it most likely is _not_ caused
> > by faulty hardware.
> >
> > The crashes are hard to reproduce, and happen about every 20 minutes
> > when compiling a kernel tree. (gcc segfaults). Sometimes the kernel
> > oopses in random places with pointer dereference faults.
> >
> > Is this a known issue?
> > I'm going to bisect this one, but it will take a lot of time, as reproducing
> > takes about 20 minutes. So that's about an hour for one test round.
> >
> > The kernel configuration is the following:
>
> Random guess:
>
> CONFIG_FRAME_POINTER=y
> CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y
>
> Note sure what those together do, check if you have any file compiled
> with -fno-omit-frame-pointer and if you do, try to change things so
> that you don't ... we found some miscompiles when that is set, exposed
> by FTRACE typically (which you don't have enabled) but possibly by other
> things.
Ok, thanks for the suggestion.
I could reproduce the crash with 2.6.26, so this is not a regression between
2.6.26 and 2.6.27-rcX.
I'm currently running longer tests on 2.6.25 again to make sure it really
isn't hardware related.
NO_NO_OMIT is a brain screwer, btw :)
--
Greetings Michael.
^ permalink raw reply
* Re: Random crashes with 2.6.27-rc3 on PPC
From: Benjamin Herrenschmidt @ 2008-08-23 22:52 UTC (permalink / raw)
To: Michael Buesch; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <200808231610.46473.mb@bu3sch.de>
On Sat, 2008-08-23 at 16:10 +0200, Michael Buesch wrote:
> I am seeing random kernel and userland application
> crashes on a Powerbook running a 2.6.27-rc3 based kernel (wireless-testing.git).
>
> The crashes did recently appear. It might be the case that they were
> introduced with the merge of 2.6.27-rc1 into wireless-testing.
> I'm not sure on that one, however. Just a guess. I still need to
> do more testing (also on vanilla upstream kernels).
>
> The crashes are completely random and they look like bad hardware.
> However I cannot reproduce on 2.6.25.9 (That's a kernel I still had
> installed, so I tried that one). So it most likely is _not_ caused
> by faulty hardware.
>
> The crashes are hard to reproduce, and happen about every 20 minutes
> when compiling a kernel tree. (gcc segfaults). Sometimes the kernel
> oopses in random places with pointer dereference faults.
>
> Is this a known issue?
> I'm going to bisect this one, but it will take a lot of time, as reproducing
> takes about 20 minutes. So that's about an hour for one test round.
>
> The kernel configuration is the following:
Random guess:
CONFIG_FRAME_POINTER=y
CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y
Note sure what those together do, check if you have any file compiled
with -fno-omit-frame-pointer and if you do, try to change things so
that you don't ... we found some miscompiles when that is set, exposed
by FTRACE typically (which you don't have enabled) but possibly by other
things.
Ben.
^ permalink raw reply
* Re: Why does one "stw" fail with address translation disabled in PPC405EP?
From: Benjamin Herrenschmidt @ 2008-08-23 22:49 UTC (permalink / raw)
To: Zhou Rui; +Cc: linuxppc-dev
In-Reply-To: <1219479992.7565.17.camel@localhost>
On Sat, 2008-08-23 at 10:26 +0200, Zhou Rui wrote:
> My board only has 32MB DRAM. Do you mean 32MB is not enough for that?
> The same codes can run well in a PPC440EP (Yosemite Board) which owns
> 256MB DRAM. At the beginning of my work, I thought memory size may be
> the cause of failure. But I did not know how to demonstrate it. So if
> the limitation of 32MB DRAM leads to the failure, are there any methods
> for the codes to solve it?
Well, it looks like the kernel is trying to access memory beyond 32M,
which would mean that the problem is that your kernel port or your
bootloader somewhat thinks there is more memory than there really is.
You need to look there... whatever passes the amount of memory to
the kernel at boot needs to be fixed.
Ben.
^ permalink raw reply
* Re: Why does one "stw" fail with address translation disabled in PPC405EP?
From: Zhou Rui @ 2008-08-23 21:18 UTC (permalink / raw)
To: Josh Boyer; +Cc: linuxppc-dev
In-Reply-To: <20080822184213.GA24526@yoda.jdub.homelinux.org>
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=UTF-8, Size: 1901 bytes --]
å¨ 2008-08-22äºç 14:42 -0400ï¼Josh Boyeråéï¼
> On Fri, Aug 22, 2008 at 08:27:15PM +0200, Zhou Rui wrote:
> >Hi, all:
> > I think I meet an odd problem with PPC405EP (PPChameleonEVB Board).
>
> What kernel version are you using?
>
> > I am running a kernel module which will execute a user space
> >application. The entry point of the application is 0x100000a0. At the
>
> That should be the first clue that you are doing it wrong. Don't do
> stuff like that in modules...
>
> >moment when the processor tries to execute the application, 0x100000a0
> >is not in TLB (this can be seen from BDI by printing out TLB entries),
> >so DTLBMiss is called automatically and then finish_tlb_load. However,
> >InstructionAccess is followed and the problem arises here.
> >InstructionAccess starts from 0x400, and after instruction "0xc0000434
> ><InstructionAccess+52>: stw r12,64(r11)", machine check occurs.
> >This instruction will store the value of r12, which is 0x0 at this
> >moment, to address 0x03072de0. I am puzzled why this action leads to
> >machine check. Is it illegal to store 0x0 in a memory address? Or is
> >there some other cause of the machine check here?
>
> I have no idea if you're using physical or virtual addresses here, so
> there isn't much we can do to help you.
>
> Do you have enough DRAM to cover that? Some of those boards only come
> with 32MiB of DRAM.
Oh, I think I just now realized that for 32MB DRAM, the physical address
should be between 0x0 and 0x1FFFFFF, but the address 0x03072da0 was out
of this range ...
To enlarge the memory size is one solution but I do not think it is
feasible for my board. Is there any solution from software side? I hope
there is.
Thank you very much!!!
Best Wishes
Zhou Rui
2008-08-23
>
> josh
__________________________________________________
¸Ï¿ì×¢²áÑÅ»¢³¬´óÈÝÁ¿Ãâ·ÑÓÊÏä?
http://cn.mail.yahoo.com
^ permalink raw reply
* Re: TLB programming in powerpc tree
From: vb @ 2008-08-23 20:22 UTC (permalink / raw)
To: Josh Boyer; +Cc: linuxppc-embedded
On Fri, Aug 22, 2008 at 6:24 AM, Josh Boyer <jwboyer@linux.vnet.ibm.com> wrote:
> On Thu, Aug 21, 2008 at 10:14:15PM -0700, vb wrote:
>>On Thu, Aug 21, 2008 at 7:29 PM, vb <vb@vsbe.com> wrote:
>>>
>>> But the main problem is that the kernel never sets up TLBs for neither
>>> the peripheral device, nor the onboard flash. I don't seem to be able
>>> to find the place where this is supposed to happen. I assumed that
>>> ioremap_nocache would take care of that, but this is not the case.
>>>
>>
>>well, in fact the TLB is set up as soon as an attempt to access the
>>peripheral is made. The problem apparently is the fact that the TLB
>>entry uses a wrong value in the nibble specifying the internal 460GT
>>block...
>
> I'm not entirely sure what you mean here. Can you elaborate a bit more?
>
I was referring to the top 4 MSB of the 36 bit address.
So, I finally was able to access that device after massaging the
device tree definitions for it, but the next device (in a different
branch of the tree) still can not be accessed. What I see is that the
tlb record is created when the access is attempted, but the top
address nibble is 0 instead of 4, even though the device tree now
includes 36 bit addresses with the correct top bits.
I am trying to find code which configures the MMU and to understand
how this whole subsystem works and where the wrong top 4 bits come
from. Is there a document describing 44x powerpc memory management?
/vb
> josh
>
^ permalink raw reply
* compile testing: cpufreq stats driver?
From: Kevin Diggs @ 2008-08-23 18:27 UTC (permalink / raw)
To: linuxppc-dev; +Cc: linux-kernel
Hi,
When I tried building my cpufreq driver into the kernel, the
cpufreq stats driver quit working? Anyone know if I screwed something up?
Or is this normal (2.6.26)?
Also, I thought, apparently incorrectly, That module parameters
became boot parameters when the module was built in to the kernel?
kevin
^ permalink raw reply
* Re: Random crashes with 2.6.27-rc3 on PPC
From: Arnaud Ebalard @ 2008-08-23 16:58 UTC (permalink / raw)
To: linuxppc-dev; +Cc: linux-kernel
In-Reply-To: <200808231610.46473.mb@bu3sch.de>
Hi,
Michael Buesch <mb@bu3sch.de> writes:
> I am seeing random kernel and userland application
> crashes on a Powerbook running a 2.6.27-rc3 based kernel
> (wireless-testing.git).
>
> The crashes did recently appear. It might be the case that they were
> introduced with the merge of 2.6.27-rc1 into wireless-testing.
> I'm not sure on that one, however. Just a guess. I still need to
> do more testing (also on vanilla upstream kernels).
>
> The crashes are completely random and they look like bad hardware.
> However I cannot reproduce on 2.6.25.9 (That's a kernel I still had
> installed, so I tried that one). So it most likely is _not_ caused
> by faulty hardware.
>
> The crashes are hard to reproduce, and happen about every 20 minutes
> when compiling a kernel tree. (gcc segfaults). Sometimes the kernel
> oopses in random places with pointer dereference faults.
Exact same thing here on a PB 12" (not a ppc64, but ppc32). It was not
on a wireless-testing.git but on a recent net-2.6 or 2.6.27-rc3 (I do
not remember precisely). I thought it was a thermal issue and did not
spend time on it because of another regression I bisected (sysctl+IPv6)
that version useless for me.
> Is this a known issue?
Now it is ;-)
> I'm going to bisect this one, but it will take a lot of time, as
> reproducing takes about 20 minutes. So that's about an hour for one
> test round.
Thanks for doing that.
Cheers,
a+
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox