public inbox for linux-pm@vger.kernel.org
 help / color / mirror / Atom feed
* Dynanic On-The-Fly Operating points for PowerOP
@ 2006-08-08 18:12 David Singleton
  2006-08-09 21:17 ` Matthew Locke
  2006-08-15 19:44 ` Pavel Machek
  0 siblings, 2 replies; 15+ messages in thread
From: David Singleton @ 2006-08-08 18:12 UTC (permalink / raw)
  To: linux-pm

The patches provided in the following three emails continue the unified,
        simplified PowerOp concept of power management.  The patches
        can be found at:

                http://source.mvista.com/~dsingleton

                        powerop-core.patch
                        powerop-cpufreq.patch
                        powerop-x86-centrino.patch


                The patches break the working PowerOP feature into
        three logical parts.  The first patch is the powerop-core.patch
        that adds support for an operating point in the standard linux
        power management infrastructure (CONFIG_PM) and adds a new
        function to perform transitioning to operating points other
        than suspend to memory or disk.

                The second patch, powerop-cpufreq.patch, adds the cpufreq
        portion of the patch that makes cpufreq tables a set of PowerOp
        operating points.

                The third patch, powerop-x86-centrino.patch, adds
        operating points for all the centrino-speedstep processors.


        This set of patches has changed in the following ways.

        1) The patch is now broken out of the cpufreq code and implements
        operating points for whatever speedstep-centrino the system
        detects upon boot.

        2) The naming scheme for operating points has been unified to
        provide a better interface to the PowerOp power manager daemon.
        The names range from:

                        highest
                        high
                        medhigh
                        medium
                        medlow
                        low
                        lowest

        PowerOp maps the supported processor frequencies onto this
        namespace list.  The set of centrino processors it supports have
        supported sets of between four and six different operating points.

        The PowerOP daemon, coming soon, can simply read the supported
        set of operating points and make some simple rules based
        decisions about when to transition to various operating points.

        The goal of a unified name space is to provide a PowerOp manager
        that runs out of the box, with very little setup by the user.


        3) This patch supports the ability to provide dynamic, on-the-fly
        operating points to the framework via a loadable module.  The 
operating
        point parameters of frequency, voltage and transition latency
        can be passed at insmod time to create any new operating point
        the centrino hardware will support.


        I think I finally understand the 'why' of hardware vendors asking
        for a requirement of dynamic, on the fly, operating points.

        I have two sets of hardware that support a wide range of
        processor speeds and voltages depending on:

        a) the rotary and dip switch setting of the board (the mainstone).

        or

        b) the revision or stepping of the hardware on the board.

        Certain revs of hardware support different frequencies and 
voltages.
        Some steppings won't run all the frequencies.

        The hardware vendors want to provide support for all the
        frequencies and voltages that the system could support,
        depending on the switch settings or rev of hardware without
        having to change kernel code and recompile the kernel.

        The new dynamic, on the fly, operating point module will allow
        for this feature.


David

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Dynanic On-The-Fly Operating points for PowerOP
  2006-08-08 18:12 Dynanic On-The-Fly Operating points for PowerOP David Singleton
@ 2006-08-09 21:17 ` Matthew Locke
  2006-08-10  4:39   ` david singleton
  2006-08-15 19:44 ` Pavel Machek
  1 sibling, 1 reply; 15+ messages in thread
From: Matthew Locke @ 2006-08-09 21:17 UTC (permalink / raw)
  To: David Singleton; +Cc: linux-pm

Dave,

I think we need to focus on defining a powerop interface that will work 
for all (or as close to all as possible) architectures and devices 
types including embedded, laptop, server.   As discussed in previous 
emails, some of the goals for powerop are:

- Architecture/board independent interface
- Integrate with clk framework (and a voltage framework in the future) 
for SoC/Board register setting abstraction
- Provide a layer that knows the SoC and board specifics about 
relationship between voltage and frequency and setting operating points 
(we call it PM Core)
- Provide a clean interface to build on top of (for cpu freq governors, 
etc).

I think we can defer the discussion around creation of op points from 
userspace, suspend/resume integration and transition notifiers.  Once 
we get the basics submitted we can add features piece by piece.

Rather than continue submitting different powerop patches I would 
encourage you to join in the discussion about the interface.  I think 
Eugeny's latest patches are pretty close to satisfying the points made 
so far.  However, we are eagerly waiting feedback because there always 
tradeoffs that need to be made when trying to satisfy the goals listed 
above.

Thanks

Matt
On Aug 8, 2006, at 11:12 AM, David Singleton wrote:

> The patches provided in the following three emails continue the 
> unified,
>         simplified PowerOp concept of power management.  The patches
>         can be found at:
>
>                 http://source.mvista.com/~dsingleton
>
>                         powerop-core.patch
>                         powerop-cpufreq.patch
>                         powerop-x86-centrino.patch
>
>
>                 The patches break the working PowerOP feature into
>         three logical parts.  The first patch is the powerop-core.patch
>         that adds support for an operating point in the standard linux
>         power management infrastructure (CONFIG_PM) and adds a new
>         function to perform transitioning to operating points other
>         than suspend to memory or disk.
>
>                 The second patch, powerop-cpufreq.patch, adds the 
> cpufreq
>         portion of the patch that makes cpufreq tables a set of PowerOp
>         operating points.
>
>                 The third patch, powerop-x86-centrino.patch, adds
>         operating points for all the centrino-speedstep processors.
>
>
>         This set of patches has changed in the following ways.
>
>         1) The patch is now broken out of the cpufreq code and 
> implements
>         operating points for whatever speedstep-centrino the system
>         detects upon boot.
>
>         2) The naming scheme for operating points has been unified to
>         provide a better interface to the PowerOp power manager daemon.
>         The names range from:
>
>                         highest
>                         high
>                         medhigh
>                         medium
>                         medlow
>                         low
>                         lowest
>
>         PowerOp maps the supported processor frequencies onto this
>         namespace list.  The set of centrino processors it supports 
> have
>         supported sets of between four and six different operating 
> points.
>
>         The PowerOP daemon, coming soon, can simply read the supported
>         set of operating points and make some simple rules based
>         decisions about when to transition to various operating points.
>
>         The goal of a unified name space is to provide a PowerOp 
> manager
>         that runs out of the box, with very little setup by the user.
>
>
>         3) This patch supports the ability to provide dynamic, 
> on-the-fly
>         operating points to the framework via a loadable module.  The
> operating
>         point parameters of frequency, voltage and transition latency
>         can be passed at insmod time to create any new operating point
>         the centrino hardware will support.
>
>
>         I think I finally understand the 'why' of hardware vendors 
> asking
>         for a requirement of dynamic, on the fly, operating points.
>
>         I have two sets of hardware that support a wide range of
>         processor speeds and voltages depending on:
>
>         a) the rotary and dip switch setting of the board (the 
> mainstone).
>
>         or
>
>         b) the revision or stepping of the hardware on the board.
>
>         Certain revs of hardware support different frequencies and
> voltages.
>         Some steppings won't run all the frequencies.
>
>         The hardware vendors want to provide support for all the
>         frequencies and voltages that the system could support,
>         depending on the switch settings or rev of hardware without
>         having to change kernel code and recompile the kernel.
>
>         The new dynamic, on the fly, operating point module will allow
>         for this feature.
>
>
> David
>
> _______________________________________________
> linux-pm mailing list
> linux-pm@lists.osdl.org
> https://lists.osdl.org/mailman/listinfo/linux-pm
>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Dynanic On-The-Fly Operating points for PowerOP
  2006-08-09 21:17 ` Matthew Locke
@ 2006-08-10  4:39   ` david singleton
  2006-08-10  7:44     ` Matthew Locke
  0 siblings, 1 reply; 15+ messages in thread
From: david singleton @ 2006-08-10  4:39 UTC (permalink / raw)
  To: Matthew Locke; +Cc: David Singleton, linux-pm


On Aug 9, 2006, at 2:17 PM, Matthew Locke wrote:

> Dave,
>
> I think we need to focus on defining a powerop interface that will 
> work for all (or as close to all as possible) architectures and 
> devices types including embedded, laptop, server.   As discussed in 
> previous emails, some of the goals for powerop are:


Matt,
	I'd hoped I had joined the discussion about the interface.  Is 
linux-pm not the right mailing list to discuss this?  I asked
Todd and he pointed me at linux-pm.

	 I find sending patches that actually work and that people can apply 
and try makes it easier to discuss and evaluate the
concepts and interfaces.  I  find it necessary to actually have working 
code to prove a concept.

I also thought the powerop write up and patches I had sent out did 
address the goals discussed so far:


>
> - Architecture/board independent interface

It has an architecture/board independent interface,  /sys/power/state, 
/sys/power/supported_states, which presents
a simplified interface to the user (and power manager).  These two 
files present the entire interface to the user.

The powerop struct provides the independent interface to the routines 
to prepare_transition, transition and
finish_transition is arch independent, through the prepare_transition, 
transition and finish_transition function pointers.
The powerop struct also provides the void *md_data pointer which keeps 
the details an arch/board's  clocks, their divisors/multipliers,
etc. in the arch/board pieces of code.

The powerop struct is arch/board independent.   The functions to 
control clocks, frequencies, voltages, etc are very
arch/board dependent, but have an arch independent interface through 
their op_vector in the powerop struct.


> - Integrate with clk framework (and a voltage framework in the future) 
> for SoC/Board register setting abstraction

This is the patch I'm working on now.  I want to actually integrate it 
and keep the boundary between arch independent
and arch/board dependent clear.

> - Provide a layer that knows the SoC and board specifics about 
> relationship between voltage and frequency and setting operating 
> points (we call it PM Core)

The patch I'm working on now shows the clock/register abstraction for 
SoC/board stuff.

The centrino abstraction examle I sent out is quite simple since the 
perfctl register msr bits can be calculated from the frequency
  and voltage.  For a more complex board there will be a lot more 
information, like clocks, their divisors/multipliers for each operating 
point, etc.

But the abstraction is the same for simple or complex boards.  Each 
operating point has an md_data pointer that
points to a struct of arch/board dependent data that the transition 
routines need.  The rest of the abstraction lies
within the three routines to prepare_transition, transition and 
finish_transition.  These routines handle the
arch/board details while the interface remains the same, the function 
pointers in each operating point


> - Provide a clean interface to build on top of (for cpu freq 
> governors, etc).

The point of the powerop patches I've sent is to make a simple 
interface for power daemons and move all the policy, class
and governor code out of the kernel and into user space, where I 
believe it belongs.

>
> I think we can defer the discussion around creation of op points from 
> userspace, suspend/resume integration and transition notifiers.  Once 
> we get the basics submitted we can add features piece by piece.

I'd prefer not to.  One of the points of sending patches that work is 
to make sure any new requirements still work without breaking
the existing framework.

>
> Rather than continue submitting different powerop patches I would 
> encourage you to join in the discussion about the interface.  I think 
> Eugeny's latest patches are pretty close to satisfying the points made 
> so far.  However, we are eagerly waiting feedback because there always 
> tradeoffs that need to be made when trying to satisfy the goals listed 
> above.

My hope is that by sending patches that work for more and more boards  
people will see that the concepts and interfaces work
across a wide range of platforms, from embedded to servers.

David
	
>
> Thanks
>
> Matt
> On Aug 8, 2006, at 11:12 AM, David Singleton wrote:
>
>> The patches provided in the following three emails continue the 
>> unified,
>>         simplified PowerOp concept of power management.  The patches
>>         can be found at:
>>
>>                 http://source.mvista.com/~dsingleton
>>
>>                         powerop-core.patch
>>                         powerop-cpufreq.patch
>>                         powerop-x86-centrino.patch
>>
>>
>>                 The patches break the working PowerOP feature into
>>         three logical parts.  The first patch is the 
>> powerop-core.patch
>>         that adds support for an operating point in the standard linux
>>         power management infrastructure (CONFIG_PM) and adds a new
>>         function to perform transitioning to operating points other
>>         than suspend to memory or disk.
>>
>>                 The second patch, powerop-cpufreq.patch, adds the 
>> cpufreq
>>         portion of the patch that makes cpufreq tables a set of 
>> PowerOp
>>         operating points.
>>
>>                 The third patch, powerop-x86-centrino.patch, adds
>>         operating points for all the centrino-speedstep processors.
>>
>>
>>         This set of patches has changed in the following ways.
>>
>>         1) The patch is now broken out of the cpufreq code and 
>> implements
>>         operating points for whatever speedstep-centrino the system
>>         detects upon boot.
>>
>>         2) The naming scheme for operating points has been unified to
>>         provide a better interface to the PowerOp power manager 
>> daemon.
>>         The names range from:
>>
>>                         highest
>>                         high
>>                         medhigh
>>                         medium
>>                         medlow
>>                         low
>>                         lowest
>>
>>         PowerOp maps the supported processor frequencies onto this
>>         namespace list.  The set of centrino processors it supports 
>> have
>>         supported sets of between four and six different operating 
>> points.
>>
>>         The PowerOP daemon, coming soon, can simply read the supported
>>         set of operating points and make some simple rules based
>>         decisions about when to transition to various operating 
>> points.
>>
>>         The goal of a unified name space is to provide a PowerOp 
>> manager
>>         that runs out of the box, with very little setup by the user.
>>
>>
>>         3) This patch supports the ability to provide dynamic, 
>> on-the-fly
>>         operating points to the framework via a loadable module.  The
>> operating
>>         point parameters of frequency, voltage and transition latency
>>         can be passed at insmod time to create any new operating point
>>         the centrino hardware will support.
>>
>>
>>         I think I finally understand the 'why' of hardware vendors 
>> asking
>>         for a requirement of dynamic, on the fly, operating points.
>>
>>         I have two sets of hardware that support a wide range of
>>         processor speeds and voltages depending on:
>>
>>         a) the rotary and dip switch setting of the board (the 
>> mainstone).
>>
>>         or
>>
>>         b) the revision or stepping of the hardware on the board.
>>
>>         Certain revs of hardware support different frequencies and
>> voltages.
>>         Some steppings won't run all the frequencies.
>>
>>         The hardware vendors want to provide support for all the
>>         frequencies and voltages that the system could support,
>>         depending on the switch settings or rev of hardware without
>>         having to change kernel code and recompile the kernel.
>>
>>         The new dynamic, on the fly, operating point module will allow
>>         for this feature.
>>
>>
>> David
>>
>> _______________________________________________
>> linux-pm mailing list
>> linux-pm@lists.osdl.org
>> https://lists.osdl.org/mailman/listinfo/linux-pm
>>
>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Dynanic On-The-Fly Operating points for PowerOP
  2006-08-10  4:39   ` david singleton
@ 2006-08-10  7:44     ` Matthew Locke
  2006-08-12  8:07       ` Vitaly Wool
  0 siblings, 1 reply; 15+ messages in thread
From: Matthew Locke @ 2006-08-10  7:44 UTC (permalink / raw)
  To: david singleton; +Cc: David Singleton, linux-pm

On Aug 9, 2006, at 9:39 PM, david singleton wrote:

>
> On Aug 9, 2006, at 2:17 PM, Matthew Locke wrote:
>
>> Dave,
>>
>> I think we need to focus on defining a powerop interface that will 
>> work for all (or as close to all as possible) architectures and 
>> devices types including embedded, laptop, server.   As discussed in 
>> previous emails, some of the goals for powerop are:
>
>
> Matt,
> 	I'd hoped I had joined the discussion about the interface.  Is 
> linux-pm not the right mailing list to discuss this?  I asked
> Todd and he pointed me at linux-pm.

Well, that is not what I meant.  Clearly this is the right list - at 
least I hope so because I haven't seen any of the cpufreq developers 
respond to yours or Eugney's patches:)

>
> 	 I find sending patches that actually work and that people can apply 
> and try makes it easier to discuss and evaluate the
> concepts and interfaces.  I  find it necessary to actually have 
> working code to prove a concept.
>

Yes, which is why Eugeny and I sent out the take 3 PowerOP patches.  A 
good discussion ensued and our updated patches are a result of that 
discussion.  If you disagree with our approach or think something is 
missing, give us some specific feedback.  Simply sending out a separate 
set of PowerOP patches is not joining in the current discussion.  It is 
confusing and probably turning people off to the ideas.   Is there 
something specific missing or wrong with the patches we submitted that 
required another set of patches to be developed?  By joining in the 
discussion, I mean that you should let us know this information.  If 
patches are your method for doing so, then at least provide a 
description of what your patches address that ours does not.  Right 
now, its just unclear why there are two different powerop patchsets.

> I also thought the powerop write up and patches I had sent out did 
> address the goals discussed so far:
>

Um,  I thought the powerop write up and  patches already sent out 
addressed the goals discussed so far.  We (everyone on the list) need 
to collaborate on the powerop effort.  Isolated development and 
attempting to discuss two separate implementations won't get us very 
far.

>
>>
>> - Architecture/board independent interface
>
> It has an architecture/board independent interface,  /sys/power/state, 
> /sys/power/supported_states, which presents
> a simplified interface to the user (and power manager).  These two 
> files present the entire interface to the user.
>
> The powerop struct provides the independent interface to the routines 
> to prepare_transition, transition and
> finish_transition is arch independent, through the prepare_transition, 
> transition and finish_transition function pointers.
> The powerop struct also provides the void *md_data pointer which keeps 
> the details an arch/board's  clocks, their divisors/multipliers,
> etc. in the arch/board pieces of code.

The transition callbacks are not needed in every operating point and 
has nothing to do with the operating point definition.  Again, if 
something is missing from the PowerOP patches, let us know.

>
> The powerop struct is arch/board independent.   The functions to 
> control clocks, frequencies, voltages, etc are very
> arch/board dependent, but have an arch independent interface through 
> their op_vector in the powerop struct.
>
>
>> - Integrate with clk framework (and a voltage framework in the 
>> future) for SoC/Board register setting abstraction
>
> This is the patch I'm working on now.  I want to actually integrate it 
> and keep the boundary between arch independent
> and arch/board dependent clear.

We already have done this.  Why duplicate design and implementation 
effort?

>
>> - Provide a layer that knows the SoC and board specifics about 
>> relationship between voltage and frequency and setting operating 
>> points (we call it PM Core)
>
> The patch I'm working on now shows the clock/register abstraction for 
> SoC/board stuff.

The kernel already has a clk framework that provides an API to set 
frequencies by clk name.  It probably could be made even more 
architecture independent.  There is no reason to start from scratch.

>
> The centrino abstraction examle I sent out is quite simple since the 
> perfctl register msr bits can be calculated from the frequency
>  and voltage.  For a more complex board there will be a lot more 
> information, like clocks, their divisors/multipliers for each 
> operating point, etc.

Right, we chose OMAP1 for the first implementation to show an example 
SoC with the lot more information.
>
> But the abstraction is the same for simple or complex boards.  Each 
> operating point has an md_data pointer that
> points to a struct of arch/board dependent data that the transition 
> routines need.  The rest of the abstraction lies
> within the three routines to prepare_transition, transition and 
> finish_transition.  These routines handle the
> arch/board details while the interface remains the same, the function 
> pointers in each operating point

The transition callbacks in the operating point are not necessary.  
Take a look at how we did this and I think you will find it much more 
flexible and applicable to a wider range of situations.  For example, 
by including frequency and voltage in the generic operating point 
structure, you are already violating the architecture/board 
independence.  Embedded SoC's, SMP systems, and MP systems will have 
multiple frequency and voltages parameters.

>
>
>> - Provide a clean interface to build on top of (for cpu freq 
>> governors, etc).
>
> The point of the powerop patches I've sent is to make a simple 
> interface for power daemons and move all the policy, class
> and governor code out of the kernel and into user space, where I 
> believe it belongs.

Agreed.  The patches we sent have the same goal.

>
>>
>> I think we can defer the discussion around creation of op points from 
>> userspace, suspend/resume integration and transition notifiers.  Once 
>> we get the basics submitted we can add features piece by piece.
>
> I'd prefer not to.  One of the points of sending patches that work is 
> to make sure any new requirements still work without breaking
> the existing framework.

The API isn't going to freeze.  Kernel internals change all the time.  
Once we have a solid first pass at the kernel portion of powerop we can 
build features and userspace interfaces on top of it.  If you have 
followed the powerop threads, then you know everyone agrees to this 
approach.

>
>>
>> Rather than continue submitting different powerop patches I would 
>> encourage you to join in the discussion about the interface.  I think 
>> Eugeny's latest patches are pretty close to satisfying the points 
>> made so far.  However, we are eagerly waiting feedback because there 
>> always tradeoffs that need to be made when trying to satisfy the 
>> goals listed above.
>
> My hope is that by sending patches that work for more and more boards  
> people will see that the concepts and interfaces work
> across a wide range of platforms, from embedded to servers.

My goal is to get PowerOP into the mainline kernel.  If everyone 
submits a different powerop implementation for those boards, then 
people will see a fragmented concept that can not be generalized.  The 
possibility of Andrew and Linus accepting PowerOP will go from high to 
never.   Again, I don't see any reason for two separate development 
efforts and patchsets.  Please stop submitting separate patches and at 
least attempt to collaborate on the current PowerOP patchsets under 
discussion.  We should be able to agree since we are both from an 
embedded background:)

I agree that we need a few more boards to prove the interface work and 
I want to see tighter integration of our PowerOP patches and cpufreq.  
Also we do need a centrino port.  If you don't have any issues with the 
interface we've presented, let's start collaborating.  Perhaps you 
could start from the next rev of patches and add centrino support?

btw, I would like see feedback on Eugeny's last set of patches.

Matt


>
> David
> 	
>>
>> Thanks
>>
>> Matt
>> On Aug 8, 2006, at 11:12 AM, David Singleton wrote:
>>
>>> The patches provided in the following three emails continue the 
>>> unified,
>>>         simplified PowerOp concept of power management.  The patches
>>>         can be found at:
>>>
>>>                 http://source.mvista.com/~dsingleton
>>>
>>>                         powerop-core.patch
>>>                         powerop-cpufreq.patch
>>>                         powerop-x86-centrino.patch
>>>
>>>
>>>                 The patches break the working PowerOP feature into
>>>         three logical parts.  The first patch is the 
>>> powerop-core.patch
>>>         that adds support for an operating point in the standard 
>>> linux
>>>         power management infrastructure (CONFIG_PM) and adds a new
>>>         function to perform transitioning to operating points other
>>>         than suspend to memory or disk.
>>>
>>>                 The second patch, powerop-cpufreq.patch, adds the 
>>> cpufreq
>>>         portion of the patch that makes cpufreq tables a set of 
>>> PowerOp
>>>         operating points.
>>>
>>>                 The third patch, powerop-x86-centrino.patch, adds
>>>         operating points for all the centrino-speedstep processors.
>>>
>>>
>>>         This set of patches has changed in the following ways.
>>>
>>>         1) The patch is now broken out of the cpufreq code and 
>>> implements
>>>         operating points for whatever speedstep-centrino the system
>>>         detects upon boot.
>>>
>>>         2) The naming scheme for operating points has been unified to
>>>         provide a better interface to the PowerOp power manager 
>>> daemon.
>>>         The names range from:
>>>
>>>                         highest
>>>                         high
>>>                         medhigh
>>>                         medium
>>>                         medlow
>>>                         low
>>>                         lowest
>>>
>>>         PowerOp maps the supported processor frequencies onto this
>>>         namespace list.  The set of centrino processors it supports 
>>> have
>>>         supported sets of between four and six different operating 
>>> points.
>>>
>>>         The PowerOP daemon, coming soon, can simply read the 
>>> supported
>>>         set of operating points and make some simple rules based
>>>         decisions about when to transition to various operating 
>>> points.
>>>
>>>         The goal of a unified name space is to provide a PowerOp 
>>> manager
>>>         that runs out of the box, with very little setup by the user.
>>>
>>>
>>>         3) This patch supports the ability to provide dynamic, 
>>> on-the-fly
>>>         operating points to the framework via a loadable module.  The
>>> operating
>>>         point parameters of frequency, voltage and transition latency
>>>         can be passed at insmod time to create any new operating 
>>> point
>>>         the centrino hardware will support.
>>>
>>>
>>>         I think I finally understand the 'why' of hardware vendors 
>>> asking
>>>         for a requirement of dynamic, on the fly, operating points.
>>>
>>>         I have two sets of hardware that support a wide range of
>>>         processor speeds and voltages depending on:
>>>
>>>         a) the rotary and dip switch setting of the board (the 
>>> mainstone).
>>>
>>>         or
>>>
>>>         b) the revision or stepping of the hardware on the board.
>>>
>>>         Certain revs of hardware support different frequencies and
>>> voltages.
>>>         Some steppings won't run all the frequencies.
>>>
>>>         The hardware vendors want to provide support for all the
>>>         frequencies and voltages that the system could support,
>>>         depending on the switch settings or rev of hardware without
>>>         having to change kernel code and recompile the kernel.
>>>
>>>         The new dynamic, on the fly, operating point module will 
>>> allow
>>>         for this feature.
>>>
>>>
>>> David
>>>
>>> _______________________________________________
>>> linux-pm mailing list
>>> linux-pm@lists.osdl.org
>>> https://lists.osdl.org/mailman/listinfo/linux-pm
>>>
>>
>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Dynanic On-The-Fly Operating points for PowerOP
  2006-08-10  7:44     ` Matthew Locke
@ 2006-08-12  8:07       ` Vitaly Wool
  2006-08-12 18:12         ` david singleton
                           ` (5 more replies)
  0 siblings, 6 replies; 15+ messages in thread
From: Vitaly Wool @ 2006-08-12  8:07 UTC (permalink / raw)
  To: Matthew Locke; +Cc: linux-pm, david singleton

> Yes, which is why Eugeny and I sent out the take 3 PowerOP patches.  A
> good discussion ensued and our updated patches are a result of that
> discussion.  If you disagree with our approach or think something is
> missing, give us some specific feedback.  Simply sending out a separate
> set of PowerOP patches is not joining in the current discussion.  It is
> confusing and probably turning people off to the ideas.   Is there
> something specific missing or wrong with the patches we submitted that
> required another set of patches to be developed?  By joining in the
> discussion, I mean that you should let us know this information.  If
> patches are your method for doing so, then at least provide a
> description of what your patches address that ours does not.  Right
> now, its just unclear why there are two different powerop patchsets.

May I disagree? Having an alternative implementation is never a bad
thing, unless the sides are unable to co-operate ;)
Let's try to compare implementations and their concepts, and benefit from both.

> Um,  I thought the powerop write up and  patches already sent out
> addressed the goals discussed so far.  We (everyone on the list) need
> to collaborate on the powerop effort.  Isolated development and
> attempting to discuss two separate implementations won't get us very
> far.

I would like to suggest both sides to think over what the main
differences of the concurring implementations are and share the
thoughts with this list. That should really be helpful to work out the
common solution.

> My goal is to get PowerOP into the mainline kernel.  If everyone
> submits a different powerop implementation for those boards, then
> people will see a fragmented concept that can not be generalized.  The
> possibility of Andrew and Linus accepting PowerOP will go from high to
> never.   Again, I don't see any reason for two separate development
> efforts and patchsets.  Please stop submitting separate patches and at
> least attempt to collaborate on the current PowerOP patchsets under
> discussion.

I wouldn't say so. I would just like to see both implementations
converging, but that is not to happen immediately. Anyway, this
implies that both authors pay more attention to the other
implementation.

Vitaly

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Dynanic On-The-Fly Operating points for PowerOP
  2006-08-12  8:07       ` Vitaly Wool
@ 2006-08-12 18:12         ` david singleton
  2006-08-12 21:32         ` david singleton
                           ` (4 subsequent siblings)
  5 siblings, 0 replies; 15+ messages in thread
From: david singleton @ 2006-08-12 18:12 UTC (permalink / raw)
  To: Vitaly Wool; +Cc: linux-pm


On Aug 12, 2006, at 1:07 AM, Vitaly Wool wrote:

>> Yes, which is why Eugeny and I sent out the take 3 PowerOP patches.  A
>> good discussion ensued and our updated patches are a result of that
>> discussion.  If you disagree with our approach or think something is
>> missing, give us some specific feedback.  Simply sending out a 
>> separate
>> set of PowerOP patches is not joining in the current discussion.  It 
>> is
>> confusing and probably turning people off to the ideas.   Is there
>> something specific missing or wrong with the patches we submitted that
>> required another set of patches to be developed?  By joining in the
>> discussion, I mean that you should let us know this information.  If
>> patches are your method for doing so, then at least provide a
>> description of what your patches address that ours does not.  Right
>> now, its just unclear why there are two different powerop patchsets.
>
> May I disagree? Having an alternative implementation is never a bad
> thing, unless the sides are unable to co-operate ;)
> Let's try to compare implementations and their concepts, and benefit 
> from both.

I agree that an alternative implementation is a good way to compare
and contrast ideas.  And Matt knows that I'm easy to work with.

The ideas I'm trying to get across are that the different power 
management
infrastructures can be unified in such a way as to benefit the kernel.
I believe the kernel would benefit from a unified power management
infrastructure and from a simplified approach to power management.

The second idea is that all operating states can be encapsulated
into a simple operating point concept.  And operating points are
created from data supplied from the hardware vendor and validated,
just as cpufreq does today.

The encapsulated operating point mechanism I'm presenting provides
an architecture independent interface to the kernel's power management
framework while providing the necessary architecture
specific functions and data to individual platforms and individual
operating points.

The powerop structure provides the architecture independent interface
to the framework.  The ops vectors provides the architecture, platform
and operating point specific interface to individual systems.  The
void *md_data pointer provides a simple way to get architecture
dependent data to the operating point functions.

The patch I have ready today shows how these architecture
independent and dependent pieces work on an x86 centrino
and on an ARM Xscale platform.

I've convinced myself that an encapsulated operating point
concept can work across different architectures and even
on very complex power management capable hardware.

Both platforms have frequency and voltage management capability,
but the Xscale has a much more complex set of information necessary
to scale frequencies.

The last idea I'm trying to present is that the simplified interface to
use the operating points will benefit the user and the
power manager daemon.  The user is presented a list of
supported operating points and uses them by writing their
name into the /sys/power/state file and the powerop framework
automagically transitions to the named operating point.

The patches are available at

http://source.mvista.com:~dsingleton/

There is the original set that worked on the x86 centrino for
2.6.18-rc1 and a new set that's incorporated suggestions
added the Xscale mainstone platform and been rolled to 2.6.18-rc4.

I'll send the individual patches out inlined in a bit.

David


I
>
>> Um,  I thought the powerop write up and  patches already sent out
>> addressed the goals discussed so far.  We (everyone on the list) need
>> to collaborate on the powerop effort.  Isolated development and
>> attempting to discuss two separate implementations won't get us very
>> far.
>
> I would like to suggest both sides to think over what the main
> differences of the concurring implementations are and share the
> thoughts with this list. That should really be helpful to work out the
> common solution.
>
>> My goal is to get PowerOP into the mainline kernel.  If everyone
>> submits a different powerop implementation for those boards, then
>> people will see a fragmented concept that can not be generalized.  The
>> possibility of Andrew and Linus accepting PowerOP will go from high to
>> never.   Again, I don't see any reason for two separate development
>> efforts and patchsets.  Please stop submitting separate patches and at
>> least attempt to collaborate on the current PowerOP patchsets under
>> discussion.
>
> I wouldn't say so. I would just like to see both implementations
> converging, but that is not to happen immediately. Anyway, this
> implies that both authors pay more attention to the other
> implementation.
>
> Vitaly

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Dynanic On-The-Fly Operating points for PowerOP
  2006-08-12  8:07       ` Vitaly Wool
  2006-08-12 18:12         ` david singleton
@ 2006-08-12 21:32         ` david singleton
  2006-08-12 21:39         ` david singleton
                           ` (3 subsequent siblings)
  5 siblings, 0 replies; 15+ messages in thread
From: david singleton @ 2006-08-12 21:32 UTC (permalink / raw)
  To: Vitaly Wool; +Cc: linux-pm

[-- Attachment #1: Type: text/plain, Size: 646 bytes --]

Here is the patch the implements PowerOp for the Intel Xscale mainstone 
platform,
as it exists in 2.6.18-rc4.

This patch implements sets of operating points that are instantiated on 
boot after
the system detects the processor type and dip and rotary switch 
settings that
affect the clock speeds.

I'm working on a patch that goes along with this patch to add the 
driver scaling
callbacks for drivers whose driving clocks are affected by frequency 
changes
in the system.  Examples are the framebuffer and LCD, serial,  pcmcia 
and mmc
drivers.

I hope to add the PowerOp driver patch to the mainstone (pxa27x) patch
in a day or so.

David



[-- Attachment #2: powerop-arm-pxa27x.patch --]
[-- Type: application/octet-stream, Size: 69902 bytes --]


Signed-Off-by: David Singleton <dsingleton@mvista.com>

 arch/arm/mach-pxa/Makefile            |    3 
 arch/arm/mach-pxa/mainstone_freq.c    |  211 +++
 arch/arm/mach-pxa/mainstone_powerop.c | 1891 ++++++++++++++++++++++++++++++++++
 arch/arm/mach-pxa/mainstone_volt.c    |  364 ++++++
 include/asm-arm/arch-pxa/powerop.h    |  119 ++
 include/asm-arm/arch-pxa/pxa-regs.h   |   12 
 6 files changed, 2595 insertions(+), 5 deletions(-)

Index: linux-2.6.17/arch/arm/mach-pxa/Makefile
===================================================================
--- linux-2.6.17.orig/arch/arm/mach-pxa/Makefile
+++ linux-2.6.17/arch/arm/mach-pxa/Makefile
@@ -10,7 +10,8 @@ obj-$(CONFIG_PXA27x) += pxa27x.o
 # Specific board support
 obj-$(CONFIG_ARCH_LUBBOCK) += lubbock.o
 obj-$(CONFIG_MACH_LOGICPD_PXA270) += lpd270.o
-obj-$(CONFIG_MACH_MAINSTONE) += mainstone.o
+obj-$(CONFIG_MACH_MAINSTONE) += mainstone.o mainstone_powerop.o \
+mainstone_freq.o  mainstone_volt.o
 obj-$(CONFIG_ARCH_PXA_IDP) += idp.o
 obj-$(CONFIG_MACH_TRIZEPS4)	+= trizeps4.o
 obj-$(CONFIG_PXA_SHARP_C7xx)	+= corgi.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o corgi_pm.o
Index: linux-2.6.17/arch/arm/mach-pxa/mainstone_freq.c
===================================================================
--- /dev/null
+++ linux-2.6.17/arch/arm/mach-pxa/mainstone_freq.c
@@ -0,0 +1,211 @@
+/*
+ * linux/arch/arm/mach-pxa/mainstone_freq.c
+ *
+ * Functions to change CPU frequencies on the Bulverde processor
+ * adopted from Intel code for MontaVista Linux.
+ *
+ * Author: <source@mvista.com>
+ *
+ * 2006 (c) MontaVista Software, Inc. This file is licensed under the
+ * terms of the GNU General Public License version 2. This program is
+ * licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/hardirq.h>
+#include <asm/io.h>
+
+#include <asm/hardware.h>
+#include <asm/arch/powerop.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+
+#include <asm/arch/mainstone.h>
+
+/*
+ * Since CPDIS and PPDIS is always the same, we use only one definition here.
+ */
+#define	PDIS	0	/* Core PLL and Peripheral PLL is enabled after FCS. */
+
+/*
+ * 	Available CPU frequency list for Bulverde.
+ */
+static unsigned int cpufreq_matrix[N_NUM][L_NUM + 1];
+static void mainstone_freq_debug_info(void);
+static volatile int *ramstart;
+
+/*
+ *  Init according to mainstone manual.
+ */
+static void mainstone_initialize_freq_matrix(void)
+{
+	int n, l;
+
+	memset(&cpufreq_matrix, 0, sizeof(cpufreq_matrix));
+
+	for (n = 2; n < N_NUM + 2; n++) {
+		for (l = 2; l <= L_NUM; l++) {
+			cpufreq_matrix[n - 2][l - 2] = (13 * n * l / 2) * 1000;
+			if (cpufreq_matrix[n - 2][l - 2] > BLVD_MAX_FREQ)
+				cpufreq_matrix[n - 2][l - 2] = 0;
+		}
+	}
+}
+
+/*
+ * This should be called with a valid freq point that was
+ * obtained via mainstone_validate_speed
+ */
+void mainstone_set_freq(unsigned int CLKCFGValue)
+{
+	unsigned long flags;
+	unsigned int unused;
+	volatile int v;
+
+	local_irq_save(flags);
+
+	/*
+	 * force a tlb fault to get the mapping into the tlb
+	 * (otherwise this will occur below when the sdram is turned off and
+	 * something-bad(tm) will happen)
+	 */
+	v = *(volatile unsigned long *)ramstart;
+	*(volatile unsigned long *)ramstart = v;
+
+	MST_LEDDAT1 = CLKCFGValue;
+
+	__asm__ __volatile__(" \n\
+		ldr	r4, [%1]			@load MDREFR \n\
+		mcr	p14, 0, %2, c6, c0, 0		@ set CCLKCFG[FCS] \n\
+		ldr	r5, =0xe3dfefff	\n\
+		and	r4, r4, r5	\n\
+		str	r4,  [%1]			@restore \n\
+		":"=&r"(unused)
+			     :"r"(&MDREFR), "r"(CLKCFGValue), "r"(ramstart)
+			     :"r4", "r5");
+
+	MST_LEDDAT1 = 0x0002;
+	/*
+	   NOTE: if we don't turn off IRQs up top, there is no point
+	   to restoring them here.
+	 */
+	local_irq_restore(flags);
+
+	/* spit out some info about what happened */
+	mainstone_freq_debug_info();
+
+}
+
+extern void mainstone_get_current_info(struct md_opt *);
+
+static void mainstone_freq_debug_info(void)
+{
+	unsigned int sysbus, run, t, turbo, mem, m = 1;
+	struct md_opt opt;
+
+	mainstone_get_current_info(&opt);
+
+	run = 13000 * opt.l;
+	turbo = (13000 * opt.l * opt.n) >> 1;
+	sysbus = (opt.b) ? run : (run / 2);
+	t = opt.regs.clkcfg & 0x1;
+
+	/* If CCCR[A] is on */
+	if (opt.cccra) {
+		mem = sysbus;
+	} else {
+		/* If A=0
+		   m initialized to 1 (for l=2-10)
+		 */
+		if (opt.l > 10)
+			m = 2;	/* for l=11-20 */
+		if (opt.l > 20)
+			m = 4;	/* for l=21-31 */
+		mem = run / m;
+	}
+}
+
+int mainstone_get_freq(void)
+{
+	unsigned int freq, n, l, ccsr;
+
+	ccsr = CCSR;
+
+	l = ccsr & CCCR_L_MASK;	/* Get L */
+	n = (ccsr & CCCR_N_MASK) >> 7;	/* Get 2N */
+
+	if (n < 2)
+		n = 2;
+
+	/* Shift to divide by 2 because N is really 2N */
+	freq = (13000 * l * n) >> 1;	/*      in kHz */
+
+	return freq;
+}
+
+unsigned int mainstone_read_clkcfg(void)
+{
+	unsigned int value = 0;
+	unsigned int un_used;
+
+	 __asm__ __volatile__("mrc	p14, 0, %0, c6, c0, 0": "=r"(value) : "r"(un_used) );
+
+	return value;
+}
+
+static int mainstone_init_freqs(void)
+{
+	int cpu_ver;
+
+	asm volatile ("mrc%? p15, 0, %0, c0, c0":"=r" (cpu_ver));
+
+	/*
+	   Bulverde     A0:     0x69054110,
+	   A1 :         0x69054111
+	 */
+	if ((cpu_ver & 0x0000f000) >> 12 == 4 &&
+	    (cpu_ver & 0xffff0000) >> 16 == 0x6905) {
+		/*    It is an xscale core bulverde chip. */
+		return 1;
+	}
+
+	return 0;
+}
+
+int mainstone_clk_init(void)
+{
+	unsigned int freq;
+
+	/*
+	 * In order to turn the sdram back on (see below) we need to
+	 * r/w the sdram.  We need to do this without the cache and
+	 * write buffer in the way.  So, we temporarily ioremap the
+	 * first page of sdram as uncached i/o memory and use the
+	 * aliased address
+	 */
+
+	/* map the first page of sdram to an uncached virtual page */
+	ramstart = (int *)ioremap(PHYS_OFFSET, 4096);
+
+	freq = mainstone_get_freq();	/*      in kHz */
+	printk(KERN_INFO "Init freq: %dkHz.\n", freq);
+
+	mainstone_initialize_freq_matrix();
+
+	if (mainstone_init_freqs()) {
+		printk(KERN_INFO "CPU frequency change initialized.\n");
+	}
+	return 0;
+}
+
+void mainstone_freq_cleanup(void)
+{
+	/* unmap the page we used*/
+	iounmap((void *)ramstart);
+}
Index: linux-2.6.17/arch/arm/mach-pxa/mainstone_powerop.c
===================================================================
--- /dev/null
+++ linux-2.6.17/arch/arm/mach-pxa/mainstone_powerop.c
@@ -0,0 +1,1891 @@
+/*
+ * arch/arm/mach-pxa/mainstone_powerop.c  PM support for Intel PXA27x
+ *
+ * Author: <source@mvista.com>
+ *
+ * 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
+ *
+ * Copyright (C) 2002, 2005, 2006 MontaVista Software <source@mvista.com>.
+ *
+ * Based on code by Matthew Locke, Dmitry Chigirev, and Bishop Brock.
+ */
+
+#include <linux/config.h>
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/pm.h>
+
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/cpufreq.h>
+
+#include <asm/uaccess.h>
+
+#include <asm/hardware.h>
+#include <asm/arch/powerop.h>
+#include <asm/arch/pxa-regs.h>
+
+static int saved_loops_per_jiffy = 0;
+static int saved_cpu_freq = 0;
+
+#define BULVERDE_DEFAULT_VOLTAGE 1400
+
+#define FSCALER_NOP	     0
+#define FSCALER_CPUFREQ	(1 << 0)
+#define FSCALER_SLEEP	  (1 << 1)
+#define FSCALER_STANDBY	(1 << 2)
+#define FSCALER_DEEPSLEEP      (1 << 3)
+#define FSCALER_WAKEUP	 (1 << 4)
+#define FSCALER_VOLTAGE	(1 << 5)
+#define FSCALER_XPLLON	  (1 << 6)
+#define FSCALER_HALFTURBO_ON   (1 << 7)
+#define FSCALER_HALFTURBO_OFF  (1 << 8)
+#define FSCALER_TURBO_ON       (1 << 9)
+#define FSCALER_TURBO_OFF      (1 << 10)
+
+#define FSCALER_TURBO (FSCALER_TURBO_ON | FSCALER_TURBO_OFF)
+
+#define FSCALER_ANY_SLEEPMODE     (FSCALER_SLEEP | \
+				   FSCALER_STANDBY | \
+				   FSCALER_DEEPSLEEP)
+
+#define CCCR_CPDIS_BIT_ON	  (1 << 31)
+#define CCCR_PPDIS_BIT_ON	  (1 << 30)
+#define CCCR_CPDIS_BIT_OFF	 (0 << 31)
+#define CCCR_PPDIS_BIT_OFF	 (0 << 30)
+#define CCCR_PLL_EARLY_EN_BIT_ON   (1 << 26)
+#define CCSR_CPLL_LOCKED	   (1 << 29)
+#define CCSR_PPLL_LOCKED	   (1 << 28)
+
+/* CLKCFG
+   | 31------------------------------------------- | 3 | 2  | 1 | 0 |
+   | --------------------------------------------- | B | HT | F | T |
+*/
+#define CLKCFG_B_BIT	       (1 << 3)
+#define CLKCFG_HT_BIT	      (1 << 2)
+#define CLKCFG_F_BIT	       (1 << 1)
+#define CLKCFG_T_BIT	       1
+
+/* Initialize the machine-dependent operating point from a list of parameters,
+   which has already been installed in the pp field of the operating point.
+   Some of the parameters may be specified with a value of -1 to indicate a
+   default value. */
+
+#define	PLL_L_MAX	31
+#define	PLL_N_MAX	8
+
+/* The MIN for L is 2 in the Yellow Book tables, but L=1 really means
+   13M mode, so L min includes 1 */
+#define	PLL_L_MIN	1
+#define	PLL_N_MIN	2
+
+/* memory timing (MSC0,DTC,DRI) constants (see Blob and Intel BBU sources) */
+#define XLLI_MSC0_13 0x11101110
+#define XLLI_MSC0_19 0x11101110
+#define XLLI_MSC0_26 0x11201120  /* 26 MHz setting */
+#define XLLI_MSC0_32 0x11201120
+#define XLLI_MSC0_39 0x11301130   /* 39 MHz setting */
+#define XLLI_MSC0_45 0x11301130
+#define XLLI_MSC0_52 0x11401140  /* @ 52 MHz setting */
+#define XLLI_MSC0_58 0x11401140
+#define XLLI_MSC0_65 0x11501150  /* @ 65 MHz setting */
+#define XLLI_MSC0_68 0x11501150
+#define XLLI_MSC0_71 0x11501150  /* @ 71.5 MHz setting */
+#define XLLI_MSC0_74 0x11601160
+#define XLLI_MSC0_78 0x12601260  /* @ 78 MHz setting */
+#define XLLI_MSC0_81 0x12601260
+#define XLLI_MSC0_84 0x12601260  /*  @ 84.5 MHz setting */
+#define XLLI_MSC0_87 0x12701270
+#define XLLI_MSC0_91 0x12701270  /* 91 MHz setting */
+#define XLLI_MSC0_94 0x12701270  /* 94.2 MHz setting */
+#define XLLI_MSC0_97 0x12701270  /* 97.5 MHz setting */
+#define XLLI_MSC0_100 0x12801280 /* 100.7 MHz setting */
+#define XLLI_MSC0_104 0x12801280 /* 104 MHz setting */
+#define XLLI_MSC0_110 0x12901290
+#define XLLI_MSC0_117 0x13901390 /* 117 MHz setting */
+#define XLLI_MSC0_124 0x13A013A0
+#define XLLI_MSC0_130 0x13A013A0 /* 130 MHz setting */
+#define XLLI_MSC0_136 0x13B013B0
+#define XLLI_MSC0_143 0x13B013B0
+#define XLLI_MSC0_149 0x13C013C0
+#define XLLI_MSC0_156 0x14C014C0
+#define XLLI_MSC0_162 0x14C014C0
+#define XLLI_MSC0_169 0x14C014C0
+#define XLLI_MSC0_175 0x14C014C0
+#define XLLI_MSC0_182 0x14C014C0
+#define XLLI_MSC0_188 0x14C014C0
+#define XLLI_MSC0_195 0x15C015C0
+#define XLLI_MSC0_201 0x15D015D0
+#define XLLI_MSC0_208 0x15D015D0
+
+/* DTC settings depend on 16/32 bit SDRAM we have (32 is chosen) */
+#define XLLI_DTC_13 0x00000000
+#define XLLI_DTC_19 0x00000000
+#define XLLI_DTC_26 0x00000000
+#define XLLI_DTC_32 0x00000000
+#define XLLI_DTC_39 0x00000000
+#define XLLI_DTC_45 0x00000000
+#define XLLI_DTC_52 0x00000000
+#define XLLI_DTC_58 0x01000100
+#define XLLI_DTC_65 0x01000100
+#define XLLI_DTC_68 0x01000100
+#define XLLI_DTC_71 0x01000100
+#define XLLI_DTC_74 0x01000100
+#define XLLI_DTC_78 0x01000100
+#define XLLI_DTC_81 0x01000100
+#define XLLI_DTC_84 0x01000100
+#define XLLI_DTC_87 0x01000100
+#define XLLI_DTC_91 0x02000200
+#define XLLI_DTC_94 0x02000200
+#define XLLI_DTC_97 0x02000200
+#define XLLI_DTC_100 0x02000200
+#define XLLI_DTC_104 0x02000200
+/* 110-208 MHz setting - SDCLK Halved*/
+#define XLLI_DTC_110 0x01000100
+#define XLLI_DTC_117 0x01000100
+#define XLLI_DTC_124 0x01000100
+#define XLLI_DTC_130 0x01000100
+#define XLLI_DTC_136 0x01000100
+#define XLLI_DTC_143 0x01000100
+#define XLLI_DTC_149 0x01000100
+#define XLLI_DTC_156 0x01000100
+#define XLLI_DTC_162 0x01000100
+#define XLLI_DTC_169 0x01000100
+#define XLLI_DTC_175 0x01000100
+/*  182-208 MHz setting - SDCLK Halved - Close to edge, so bump up */
+#define XLLI_DTC_182 0x02000200
+#define XLLI_DTC_188 0x02000200
+#define XLLI_DTC_195 0x02000200
+#define XLLI_DTC_201 0x02000200
+#define XLLI_DTC_208 0x02000200
+
+/*       Optimal values for DRI (refreash interval) settings for
+ * various MemClk settings (MDREFR)
+ */
+#define XLLI_DRI_13 0x002
+#define XLLI_DRI_19 0x003
+#define XLLI_DRI_26 0x005
+#define XLLI_DRI_32 0x006
+#define XLLI_DRI_39 0x008
+#define XLLI_DRI_45 0x00A
+#define XLLI_DRI_52 0x00B
+#define XLLI_DRI_58 0x00D
+#define XLLI_DRI_65 0x00E
+#define XLLI_DRI_68 0x00F
+#define XLLI_DRI_71 0x010
+#define XLLI_DRI_74 0x011
+#define XLLI_DRI_78 0x012
+#define XLLI_DRI_81 0x012
+#define XLLI_DRI_84 0x013
+#define XLLI_DRI_87 0x014
+#define XLLI_DRI_91 0x015
+#define XLLI_DRI_94 0x016
+#define XLLI_DRI_97 0x016
+#define XLLI_DRI_100 0x017
+#define XLLI_DRI_104 0x018
+#define XLLI_DRI_110 0x01A
+#define XLLI_DRI_117 0x01B
+#define XLLI_DRI_124 0x01D
+#define XLLI_DRI_130 0x01E
+#define XLLI_DRI_136 0x020
+#define XLLI_DRI_143 0x021
+#define XLLI_DRI_149 0x023
+#define XLLI_DRI_156 0x025
+#define XLLI_DRI_162 0x026
+#define XLLI_DRI_169 0x028
+#define XLLI_DRI_175 0x029
+#define XLLI_DRI_182 0x02B
+#define XLLI_DRI_188 0x02D
+#define XLLI_DRI_195 0x02E
+#define XLLI_DRI_201 0x030
+#define XLLI_DRI_208 0x031
+
+
+
+/* timings for memory controller set up (masked values) */
+struct mem_timings{
+	unsigned int msc0; /* for MSC0 */
+	unsigned int dtc; /* for MDCNFG */
+	unsigned int dri; /* for MDREFR */
+};
+
+static int pxa27x_prepare_transition(struct powerop *cur, struct powerop *new);
+static int pxa27x_transition(struct powerop *cur, struct powerop *new);
+static int pxa27x_finish_transition(struct powerop *cur, struct powerop *new);
+
+static struct powerop sleep = {
+	.name = "sleep",
+	.description = "Mainstone Sleep state",
+	.type = PM_FREQ_CHANGE,
+	.prepare_transition  = pxa27x_prepare_transition,
+	.transition = pxa27x_transition,
+	.finish_transition = pxa27x_finish_transition,
+};
+
+static struct powerop deepsleep = {
+	.name = "deepsleep",
+	.description = "Mainstone Deep Sleep mode",
+	.type = PM_FREQ_CHANGE,
+	.prepare_transition  = pxa27x_prepare_transition,
+	.transition = pxa27x_transition,
+	.finish_transition = pxa27x_finish_transition,
+};
+
+static struct powerop standby = {
+	.name = "pxa27x_standby",
+	.description = "MainstoneStandby mode",
+	.type = PM_FREQ_CHANGE,
+	.prepare_transition  = pxa27x_prepare_transition,
+	.transition = pxa27x_transition,
+	.finish_transition = pxa27x_finish_transition,
+};
+
+static struct powerop lowest = {
+	.name = "lowest",
+	.description = "Lowest Frequency state",
+	.type = PM_FREQ_CHANGE,
+	.frequency = 0,
+	.voltage = 0,
+	.latency = 100,
+	.prepare_transition  = pxa27x_prepare_transition,
+	.transition = pxa27x_transition,
+	.finish_transition = pxa27x_finish_transition,
+};
+
+static struct powerop low = {
+	.name = "low",
+	.description = "Low Frequency state",
+	.type = PM_FREQ_CHANGE,
+	.frequency = 0,
+	.voltage = 0,
+	.latency = 100,
+	.prepare_transition  = pxa27x_prepare_transition,
+	.transition = pxa27x_transition,
+	.finish_transition = pxa27x_finish_transition,
+};
+
+static struct powerop mediumlow = {
+	.name = "mediumlow",
+	.description = "Medium Low Frequency state",
+	.type = PM_FREQ_CHANGE,
+	.frequency = 0,
+	.voltage = 0,
+	.latency = 100,
+	.prepare_transition  = pxa27x_prepare_transition,
+	.transition = pxa27x_transition,
+	.finish_transition = pxa27x_finish_transition,
+};
+
+static struct powerop medium = {
+	.name = "medium",
+	.description = "Medium Frequency state",
+	.type = PM_FREQ_CHANGE,
+	.frequency = 0,
+	.voltage = 0,
+	.latency = 100,
+	.prepare_transition  = pxa27x_prepare_transition,
+	.transition = pxa27x_transition,
+	.finish_transition = pxa27x_finish_transition,
+};
+
+static struct powerop mediumhigh = {
+	.name = "mediumhigh",
+	.description = "Medium High Frequency state",
+	.type = PM_FREQ_CHANGE,
+	.frequency = 0,
+	.voltage = 0,
+	.latency = 100,
+	.prepare_transition  = pxa27x_prepare_transition,
+	.transition = pxa27x_transition,
+	.finish_transition = pxa27x_finish_transition,
+};
+
+static struct powerop high = {
+	.name = "high",
+	.description = "High Frequency state",
+	.type = PM_FREQ_CHANGE,
+	.frequency = 0,
+	.voltage = 0,
+	.latency = 100,
+	.prepare_transition  = pxa27x_prepare_transition,
+	.transition = pxa27x_transition,
+	.finish_transition = pxa27x_finish_transition,
+};
+
+static struct powerop highest = {
+	.name = "highest",
+	.description = "Highest Frequency state",
+	.type = PM_FREQ_CHANGE,
+	.frequency = 0,
+	.voltage = 0,
+	.latency = 100,
+	.prepare_transition  = pxa27x_prepare_transition,
+	.transition = pxa27x_transition,
+	.finish_transition = pxa27x_finish_transition,
+};
+
+static struct md_opt mhz104 = {
+	.v = 900,
+	.l = 8,
+	.n = 2,
+	.b = 1,
+	.half_turbo = 0,
+	.cccra = 1,
+	.cpll_enabled = 1,
+	.ppll_enabled = 1,
+	.sleep_mode = 0,
+};
+
+static struct md_opt mhz208 = {
+	.v = 1050,
+	.l = 8,
+	.n = 2,
+	.b = 1,
+	.half_turbo = 0,
+	.cccra = 0,
+	.cpll_enabled = 1,
+	.ppll_enabled = 1,
+	.sleep_mode = 0,
+};
+
+static struct md_opt mhz312 = {
+	.v = 1250,
+	.l = 16,
+	.n = 3,
+	.b = 1,
+	.half_turbo = 0,
+	.cccra = 0,
+	.cpll_enabled = 1,
+	.ppll_enabled = 1,
+	.sleep_mode = 0,
+};
+
+static struct md_opt mhz208hi = {
+	.v = 1150,
+	.l = 16,
+	.n = 2,
+	.b = 1,
+	.half_turbo = 0,
+	.cccra = 1,
+	.cpll_enabled = 1,
+	.ppll_enabled = 1,
+	.sleep_mode = 0,
+};
+
+static struct md_opt mhz312hi = {
+	.v = 1250,
+	.l = 16,
+	.n = 3,
+	.b = 1,
+	.half_turbo = 0,
+	.cccra = 1,
+	.cpll_enabled = 1,
+	.ppll_enabled = 1,
+	.sleep_mode = 0,
+};
+
+static struct md_opt mhz416hi = {
+	.v = 1350,
+	.l = 16,
+	.n = 4,
+	.b = 1,
+	.half_turbo = 0,
+	.cccra = 1,
+	.cpll_enabled = 1,
+	.ppll_enabled = 1,
+	.sleep_mode = 0,
+};
+
+static struct md_opt mhz520hi = {
+	.v = 1450,
+	.l = 16,
+	.n = 5,
+	.b = 1,
+	.half_turbo = 0,
+	.cccra = 1,
+	.cpll_enabled = 1,
+	.ppll_enabled = 1,
+	.sleep_mode = 0,
+};
+
+static struct md_opt md_sleep = {
+	.v = 1500,
+	.l = 0,
+	.n = 0,
+	.b = 0,
+	.half_turbo = -1,
+	.cccra = 0,
+	.cpll_enabled = 0,
+	.ppll_enabled = 0,
+	.sleep_mode = 3,
+};
+
+static struct md_opt md_deepsleep = {
+	.v = 1500,
+	.l = 0,
+	.n = 0,
+	.b = 0,
+	.half_turbo = -1,
+	.cccra = 0,
+	.cpll_enabled = 0,
+	.ppll_enabled = 0,
+	.sleep_mode = 7,
+};
+
+static struct md_opt md_standby = {
+	.v = 1500,
+	.l = 1,
+	.n = 2,
+	.b = 0,
+	.half_turbo = -1,
+	.cccra = 0,
+	.cpll_enabled = 0,
+	.ppll_enabled = 0,
+	.sleep_mode = -1,
+};
+
+static void
+mainstone_setup_opt(struct powerop *op, uint freq, struct md_opt *md)
+{
+	op->frequency = freq * 1000;
+	op->md_data = (void *)md;
+	op->voltage = md->v;
+}
+
+static unsigned int fscaler_flags = 0;
+
+void mainstone_fully_define_opt(struct powerop *cur,
+				   struct powerop *new);
+static int mainstone_fscale(struct powerop *cur, struct powerop *new);
+static void mainstone_fscaler(struct powerop_regs *regs);
+
+extern void mainstone_set_voltage(unsigned int mv);
+extern void mainstone_prep_set_voltage(unsigned int mv);
+extern int mainstone_vcs_init(void);
+extern void mainstone_voltage_cleanup(void);
+
+static unsigned long
+calculate_memclk(unsigned long cccr, unsigned long clkcfg)
+{
+	unsigned long M, memclk;
+	u32 L;
+
+	L = cccr & 0x1f;
+	if (cccr & (1 << 25)) {
+		if  (clkcfg & CLKCFG_B_BIT)
+			memclk = (L*13);
+		else
+			memclk = (L*13)/2;
+	}
+	else {
+		if (L <= 10) M = 1;
+		else if (L <= 20) M = 2;
+		else M = 4;
+
+		memclk = (L*13)/M;
+	}
+
+	return memclk;
+}
+
+static unsigned long
+calculate_new_memclk(struct powerop_regs *regs)
+{
+	return calculate_memclk(regs->cccr, regs->clkcfg);
+}
+
+static unsigned long
+calculate_cur_memclk(void)
+{
+	unsigned long cccr = CCCR;
+	return calculate_memclk(cccr, mainstone_read_clkcfg());
+}
+
+/* Returns optimal timings for memory controller
+ * a - [A]
+ * b - [B]
+ * l - value of L
+ */
+static struct mem_timings get_optimal_mem_timings(int a, int b, int l){
+	struct mem_timings ret = {
+		.msc0 = 0,
+		.dtc = 0,
+		.dri = 0,
+	};
+
+	if (a!=0 && b==0) {
+		switch (l) {
+		case 2:
+			ret.msc0 = XLLI_MSC0_13;
+			ret.dtc = XLLI_DTC_13;
+			ret.dri = XLLI_DRI_13;
+			break;
+		case 3:
+			ret.msc0 = XLLI_MSC0_19;
+			ret.dtc = XLLI_DTC_19;
+			ret.dri = XLLI_DRI_19;
+			break;
+		case 4:
+			ret.msc0 = XLLI_MSC0_26;
+			ret.dtc = XLLI_DTC_26;
+			ret.dri = XLLI_DRI_26;
+			break;
+		case 5:
+			ret.msc0 = XLLI_MSC0_32;
+			ret.dtc = XLLI_DTC_32;
+			ret.dri = XLLI_DRI_32;
+			break;
+		case 6:
+			ret.msc0 = XLLI_MSC0_39;
+			ret.dtc = XLLI_DTC_39;
+			ret.dri = XLLI_DRI_39;
+			break;
+		case 7:
+			ret.msc0 = XLLI_MSC0_45;
+			ret.dtc = XLLI_DTC_45;
+			ret.dri = XLLI_DRI_45;
+			break;
+		case 8:
+			ret.msc0 = XLLI_MSC0_52;
+			ret.dtc = XLLI_DTC_52;
+			ret.dri = XLLI_DRI_52;
+			break;
+		case 9:
+			ret.msc0 = XLLI_MSC0_58;
+			ret.dtc = XLLI_DTC_58;
+			ret.dri = XLLI_DRI_58;
+			break;
+		case 10:
+			ret.msc0 = XLLI_MSC0_65;
+			ret.dtc = XLLI_DTC_65;
+			ret.dri = XLLI_DRI_65;
+			break;
+			/*
+			 *       L11 - L20 ARE THE SAME for A0Bx
+			 */
+		case 11:
+			ret.msc0 = XLLI_MSC0_71;
+			ret.dtc = XLLI_DTC_71;
+			ret.dri = XLLI_DRI_71;
+			break;
+		case 12:
+			ret.msc0 = XLLI_MSC0_78;
+			ret.dtc = XLLI_DTC_78;
+			ret.dri = XLLI_DRI_78;
+			break;
+		case 13:
+			ret.msc0 = XLLI_MSC0_84;
+			ret.dtc = XLLI_DTC_84;
+			ret.dri = XLLI_DRI_84;
+			break;
+		case 14:
+			ret.msc0 = XLLI_MSC0_91;
+			ret.dtc = XLLI_DTC_91;
+			ret.dri = XLLI_DRI_91;
+			break;
+		case 15:
+			ret.msc0 = XLLI_MSC0_97;
+			ret.dtc = XLLI_DTC_97;
+			ret.dri = XLLI_DRI_97;
+			break;
+		case 16:
+			ret.msc0 = XLLI_MSC0_104;
+			ret.dtc = XLLI_DTC_104;
+			ret.dri = XLLI_DRI_104;
+			break;
+		case 17:
+			ret.msc0 = XLLI_MSC0_110;
+			ret.dtc = XLLI_DTC_110;
+			ret.dri = XLLI_DRI_110;
+			break;
+		case 18:
+			ret.msc0 = XLLI_MSC0_117;
+			ret.dtc = XLLI_DTC_117;
+			ret.dri = XLLI_DRI_117;
+			break;
+		case 19:
+			ret.msc0 = XLLI_MSC0_124;
+			ret.dtc = XLLI_DTC_124;
+			ret.dri = XLLI_DRI_124;
+			break;
+		case 20:
+			ret.msc0 = XLLI_MSC0_130;
+			ret.dtc = XLLI_DTC_130;
+			ret.dri = XLLI_DRI_130;
+			break;
+		case 21:
+			ret.msc0 = XLLI_MSC0_136;
+			ret.dtc = XLLI_DTC_136;
+			ret.dri = XLLI_DRI_136;
+			break;
+		case 22:
+			ret.msc0 = XLLI_MSC0_143;
+			ret.dtc = XLLI_DTC_143;
+			ret.dri = XLLI_DRI_143;
+			break;
+		case 23:
+			ret.msc0 = XLLI_MSC0_149;
+			ret.dtc = XLLI_DTC_149;
+			ret.dri = XLLI_DRI_149;
+			break;
+		case 24:
+			ret.msc0 = XLLI_MSC0_156;
+			ret.dtc = XLLI_DTC_156;
+			ret.dri = XLLI_DRI_156;
+			break;
+		case 25:
+			ret.msc0 = XLLI_MSC0_162;
+			ret.dtc = XLLI_DTC_162;
+			ret.dri = XLLI_DRI_162;
+			break;
+		case 26:
+			ret.msc0 = XLLI_MSC0_169;
+			ret.dtc = XLLI_DTC_169;
+			ret.dri = XLLI_DRI_169;
+			break;
+		case 27:
+			ret.msc0 = XLLI_MSC0_175;
+			ret.dtc = XLLI_DTC_175;
+			ret.dri = XLLI_DRI_175;
+			break;
+		case 28:
+			ret.msc0 = XLLI_MSC0_182;
+			ret.dtc = XLLI_DTC_182;
+			ret.dri = XLLI_DRI_182;
+			break;
+		case 29:
+			ret.msc0 = XLLI_MSC0_188;
+			ret.dtc = XLLI_DTC_188;
+			ret.dri = XLLI_DRI_188;
+			break;
+		case 30:
+			ret.msc0 = XLLI_MSC0_195;
+			ret.dtc = XLLI_DTC_195;
+			ret.dri = XLLI_DRI_195;
+			break;
+		case 31:
+			ret.msc0 = XLLI_MSC0_201;
+			ret.dtc = XLLI_DTC_201;
+			ret.dri = XLLI_DRI_201;
+		}
+
+	} else if (a!=0 && b!=0) {
+	  switch (l) {
+		case 2:
+			ret.msc0 = XLLI_MSC0_26;
+			ret.dtc = XLLI_DTC_26;
+			ret.dri = XLLI_DRI_26;
+			break;
+		case 3:
+			ret.msc0 = XLLI_MSC0_39;
+			ret.dtc = XLLI_DTC_39;
+			ret.dri = XLLI_DRI_39;
+			break;
+		case 4:
+			ret.msc0 = XLLI_MSC0_52;
+			ret.dtc = XLLI_DTC_52;
+			ret.dri = XLLI_DRI_52;
+			break;
+		case 5:
+			ret.msc0 = XLLI_MSC0_65;
+			ret.dtc = XLLI_DTC_65;
+			ret.dri = XLLI_DRI_65;
+			break;
+		case 6:
+			ret.msc0 = XLLI_MSC0_78;
+			ret.dtc = XLLI_DTC_78;
+			ret.dri = XLLI_DRI_78;
+			break;
+		case 7:
+			ret.msc0 = XLLI_MSC0_91;
+			ret.dtc = XLLI_DTC_91;
+			ret.dri = XLLI_DRI_91;
+			break;
+		case 8:
+			ret.msc0 = XLLI_MSC0_104;
+			ret.dtc = XLLI_DTC_104;
+			ret.dri = XLLI_DRI_104;
+			break;
+		case 9:
+			ret.msc0 = XLLI_MSC0_117;
+			ret.dtc = XLLI_DTC_117;
+			ret.dri = XLLI_DRI_117;
+			break;
+		case 10:
+			ret.msc0 = XLLI_MSC0_130;
+			ret.dtc = XLLI_DTC_130;
+			ret.dri = XLLI_DRI_130;
+			break;
+		case 11:
+			ret.msc0 = XLLI_MSC0_143;
+			ret.dtc = XLLI_DTC_143;
+			ret.dri = XLLI_DRI_143;
+			break;
+		case 12:
+			ret.msc0 = XLLI_MSC0_156;
+			ret.dtc = XLLI_DTC_156;
+			ret.dri = XLLI_DRI_156;
+			break;
+		case 13:
+			ret.msc0 = XLLI_MSC0_169;
+			ret.dtc = XLLI_DTC_169;
+			ret.dri = XLLI_DRI_169;
+			break;
+		case 14:
+			ret.msc0 = XLLI_MSC0_182;
+			ret.dtc = XLLI_DTC_182;
+			ret.dri = XLLI_DRI_182;
+			break;
+		case 15:
+			ret.msc0 = XLLI_MSC0_195;
+			ret.dtc = XLLI_DTC_195;
+			ret.dri = XLLI_DRI_195;
+			break;
+		case 16:
+			ret.msc0 = XLLI_MSC0_208;
+			ret.dtc = XLLI_DTC_208;
+			ret.dri = XLLI_DRI_208;
+		}
+	} else {
+	  /* A0Bx */
+		switch (l) {
+		case 2:
+			ret.msc0 = XLLI_MSC0_26;
+			ret.dtc = XLLI_DTC_26;
+			ret.dri = XLLI_DRI_26;
+			break;
+		case 3:
+			ret.msc0 = XLLI_MSC0_39;
+			ret.dtc = XLLI_DTC_39;
+			ret.dri = XLLI_DRI_39;
+			break;
+		case 4:
+			ret.msc0 = XLLI_MSC0_52;
+			ret.dtc = XLLI_DTC_52;
+			ret.dri = XLLI_DRI_52;
+			break;
+		case 5:
+			ret.msc0 = XLLI_MSC0_65;
+			ret.dtc = XLLI_DTC_65;
+			ret.dri = XLLI_DRI_65;
+			break;
+		case 6:
+			ret.msc0 = XLLI_MSC0_78;
+			ret.dtc = XLLI_DTC_78;
+			ret.dri = XLLI_DRI_78;
+			break;
+		case 7:
+			ret.msc0 = XLLI_MSC0_91;
+			ret.dtc = XLLI_DTC_91;
+			ret.dri = XLLI_DRI_91;
+			break;
+		case 8:
+			ret.msc0 = XLLI_MSC0_104;
+			ret.dtc = XLLI_DTC_104;
+			ret.dri = XLLI_DRI_104;
+			break;
+		case 9:
+			ret.msc0 = XLLI_MSC0_117;
+			ret.dtc = XLLI_DTC_117;
+			ret.dri = XLLI_DRI_117;
+			break;
+		case 10:
+			ret.msc0 = XLLI_MSC0_130;
+			ret.dtc = XLLI_DTC_130;
+			ret.dri = XLLI_DRI_130;
+			break;
+		case 11:
+			ret.msc0 = XLLI_MSC0_71;
+			ret.dtc = XLLI_DTC_71;
+			ret.dri = XLLI_DRI_71;
+			break;
+		case 12:
+			ret.msc0 = XLLI_MSC0_78;
+			ret.dtc = XLLI_DTC_78;
+			ret.dri = XLLI_DRI_78;
+			break;
+		case 13:
+			ret.msc0 = XLLI_MSC0_84;
+			ret.dtc = XLLI_DTC_84;
+			ret.dri = XLLI_DRI_84;
+			break;
+		case 14:
+			ret.msc0 = XLLI_MSC0_91;
+			ret.dtc = XLLI_DTC_91;
+			ret.dri = XLLI_DRI_91;
+			break;
+		case 15:
+			ret.msc0 = XLLI_MSC0_97;
+			ret.dtc = XLLI_DTC_97;
+			ret.dri = XLLI_DRI_97;
+			break;
+		case 16:
+			ret.msc0 = XLLI_MSC0_104;
+			ret.dtc = XLLI_DTC_104;
+			ret.dri = XLLI_DRI_104;
+			break;
+		case 17:
+			ret.msc0 = XLLI_MSC0_110;
+			ret.dtc = XLLI_DTC_110;
+			ret.dri = XLLI_DRI_110;
+			break;
+		case 18:
+			ret.msc0 = XLLI_MSC0_117;
+			ret.dtc = XLLI_DTC_117;
+			ret.dri = XLLI_DRI_117;
+			break;
+		case 19:
+			ret.msc0 = XLLI_MSC0_124;
+			ret.dtc = XLLI_DTC_124;
+			ret.dri = XLLI_DRI_124;
+			break;
+		case 20:
+			ret.msc0 = XLLI_MSC0_130;
+			ret.dtc = XLLI_DTC_130;
+			ret.dri = XLLI_DRI_130;
+			break;
+		case 21:
+			ret.msc0 = XLLI_MSC0_68;
+			ret.dtc = XLLI_DTC_68;
+			ret.dri = XLLI_DRI_68;
+			break;
+		case 22:
+			ret.msc0 = XLLI_MSC0_71;
+			ret.dtc = XLLI_DTC_71;
+			ret.dri = XLLI_DRI_71;
+			break;
+		case 23:
+			ret.msc0 = XLLI_MSC0_74;
+			ret.dtc = XLLI_DTC_74;
+			ret.dri = XLLI_DRI_74;
+			break;
+		case 24:
+			ret.msc0 = XLLI_MSC0_78;
+			ret.dtc = XLLI_DTC_78;
+			ret.dri = XLLI_DRI_78;
+			break;
+		case 25:
+			ret.msc0 = XLLI_MSC0_81;
+			ret.dtc = XLLI_DTC_81;
+			ret.dri = XLLI_DRI_81;
+			break;
+		case 26:
+			ret.msc0 = XLLI_MSC0_84;
+			ret.dtc = XLLI_DTC_84;
+			ret.dri = XLLI_DRI_84;
+			break;
+		case 27:
+			ret.msc0 = XLLI_MSC0_87;
+			ret.dtc = XLLI_DTC_87;
+			ret.dri = XLLI_DRI_87;
+			break;
+		case 28:
+			ret.msc0 = XLLI_MSC0_91;
+			ret.dtc = XLLI_DTC_91;
+			ret.dri = XLLI_DRI_91;
+			break;
+		case 29:
+			ret.msc0 = XLLI_MSC0_94;
+			ret.dtc = XLLI_DTC_94;
+			ret.dri = XLLI_DRI_94;
+			break;
+		case 30:
+			ret.msc0 = XLLI_MSC0_97;
+			ret.dtc = XLLI_DTC_97;
+			ret.dri = XLLI_DRI_97;
+			break;
+		case 31:
+			ret.msc0 = XLLI_MSC0_100;
+			ret.dtc = XLLI_DTC_100;
+			ret.dri = XLLI_DRI_100;
+		}
+	}
+
+	return ret;
+}
+
+static void assign_optimal_mem_timings(
+	unsigned int* msc0_reg,
+	unsigned int* mdrefr_reg,
+	unsigned int* mdcnfg_reg,
+	int a, int b, int l
+	)
+{
+	unsigned int msc0_reg_tmp = (*msc0_reg);
+	unsigned int mdrefr_reg_tmp = (*mdrefr_reg);
+	unsigned int mdcnfg_reg_tmp = (*mdcnfg_reg);
+	struct mem_timings timings = get_optimal_mem_timings(a,b,l);
+
+	/* clear bits which are set by get_optimal_mem_timings*/
+	msc0_reg_tmp &= ~(MSC0_RDF & MSC0_RDN & MSC0_RRR);
+	mdrefr_reg_tmp &= ~(MDREFR_RFU & MDREFR_DRI);
+	mdcnfg_reg_tmp &= ~(MDCNFG_DTC0 & MDCNFG_DTC2);
+
+	/* prepare appropriate timings */
+	msc0_reg_tmp |= timings.msc0;
+	mdrefr_reg_tmp |= timings.dri;
+	mdcnfg_reg_tmp |= timings.dtc;
+
+	/* set timings (all bits one time) */
+	(*msc0_reg) = msc0_reg_tmp;
+	(*mdrefr_reg) = mdrefr_reg_tmp;
+	(*mdcnfg_reg) = mdcnfg_reg_tmp;
+}
+
+static void set_mdrefr_value(u32 new_mdrefr)
+{
+	unsigned long s, old_mdrefr, errata62;
+	old_mdrefr = MDREFR;
+	/* E62 (28007106.pdf): Memory controller may hang while clearing
+	 * MDREFR[K1DB2] or MDREFR[K2DB2]
+	 */
+	errata62 = (((old_mdrefr & MDREFR_K1DB2) != 0) &&
+		((new_mdrefr & MDREFR_K1DB2) == 0)) ||
+		(((old_mdrefr & MDREFR_K2DB2) != 0) &&
+		((new_mdrefr & MDREFR_K2DB2) == 0));
+
+	if (errata62) {
+		unsigned long oscr_0 = OSCR;
+		unsigned long oscr_1 = oscr_0;
+		/* Step 1 - disable interrupts */
+		local_irq_save(s);
+		/* Step 2 - leave KxDB2, but set MDREFR[DRI] (bits 0-11) to
+		 *  0xFFF
+		 */
+		MDREFR = MDREFR | MDREFR_DRI;
+		/* Step 3 - read MDREFR one time */
+		MDREFR;
+		/* Step 4 - wait 1.6167us
+		 * (3.25MHz clock increments OSCR0 7 times)
+		 */
+		while (oscr_1-oscr_0 < 7) {
+			cpu_relax();
+			oscr_1 = OSCR;
+		}
+
+	}
+
+	/* Step 5 - clear K1DB1 and/or K2DB2, and set MDREFR[DRI] to
+	 * proper value at the same time
+	 */
+
+	/*Set MDREFR as if no errata workaround is needed*/
+	MDREFR = new_mdrefr;
+
+	if (errata62) {
+		/* Step 6 - read MDREFR one time*/
+		MDREFR;
+		/* Step 7 - enable interrupts*/
+		local_irq_restore(s);
+	}
+}
+
+static void mainstone_scale_cpufreq(struct powerop_regs *regs)
+{
+	unsigned long new_memclk, cur_memclk;
+	u32 new_mdrefr, cur_mdrefr, read_mdrefr;
+	u32 new_msc0, new_mdcnfg;
+	int set_mdrefr = 0, scaling_up = 0;
+	int l, a, b;
+
+	l = regs->cccr & CCCR_L_MASK;	/* Get L */
+	b = (regs->clkcfg >> 3) & 0x1;
+	a = (regs->cccr >> 25) & 0x1;	/* cccr[A]: bit 25 */
+
+	cur_memclk = calculate_cur_memclk();
+	new_memclk = calculate_new_memclk(regs);
+
+	new_mdrefr = cur_mdrefr = MDREFR;
+	new_msc0 = MSC0;
+	new_mdcnfg = MDCNFG;
+
+	if (new_memclk != cur_memclk) {
+		/* SDCLK0,SDCLK1,SDCLK2 = MEMCLK - by default (<=52MHz) */
+		new_mdrefr &= ~( MDREFR_K0DB2 | MDREFR_K0DB4 |
+			MDREFR_K1DB2 | MDREFR_K2DB2 );
+
+		if ((new_memclk > 52) && (new_memclk <= 104)) {
+			/* SDCLK0 = MEMCLK/2, SDCLK1,SDCLK2 = MEMCLK */
+			new_mdrefr |= MDREFR_K0DB2;
+		}
+		else if (new_memclk > 104){
+			/* SDCLK0 = MEMCLK/4,  SDCLK1 and SDCLK2 = MEMCLK/2 */
+			new_mdrefr |= (MDREFR_K0DB4 | MDREFR_K1DB2 | MDREFR_K2DB2);
+		}
+
+		/* clock increasing or decreasing? */
+		if (new_memclk > cur_memclk) scaling_up = 1;
+	}
+
+	/* set MDREFR if necessary */
+	if (new_mdrefr != cur_mdrefr){
+		set_mdrefr = 1;
+		/* also adjust timings  as long as we change MDREFR value */
+		assign_optimal_mem_timings(
+					   &new_msc0,
+					   &new_mdrefr,
+					   &new_mdcnfg,
+					   a,b,l
+					   );
+	}
+
+	/* if memclk is scaling up, set MDREFR before freq change
+	 * (2800002.pdf:6.5.1.4)
+	 */
+	if (set_mdrefr && scaling_up) {
+		MSC0 = new_msc0;
+		set_mdrefr_value(new_mdrefr);
+		MDCNFG = new_mdcnfg;
+		read_mdrefr = MDREFR;
+	}
+
+	CCCR = regs->cccr;
+	mainstone_set_freq(regs->clkcfg);
+
+	/* if memclk is scaling down, set MDREFR after freq change
+	 * (2800002.pdf:6.5.1.4)
+	 */
+	if (set_mdrefr && !scaling_up) {
+		MSC0 = new_msc0;
+		set_mdrefr_value(new_mdrefr);
+		MDCNFG = new_mdcnfg;
+		read_mdrefr = MDREFR;
+	}
+}
+
+static void mainstone_scale_voltage(struct powerop_regs *regs)
+{
+	mainstone_set_voltage(regs->voltage);
+}
+
+static void mainstone_scale_voltage_coupled(struct powerop_regs *regs)
+{
+	mainstone_prep_set_voltage(regs->voltage);
+}
+
+static void calculate_lcd_freq(struct md_opt *opt)
+{
+	int k = 1;		/* lcd divisor */
+
+	/* L is verified to be between PLL_L_MAX and PLL_L_MIN in */
+	if (opt->l == -1) {
+		opt->lcd = -1;
+		return;
+	}
+
+	if (opt->l > 16) {
+		/* When L=17-31, K=4 */
+		k = 4;
+	} else if (opt->l > 7) {
+		/* When L=8-16, K=2 */
+		k = 2;
+	}
+
+	/* Else, when L=2-7, K=1 */
+
+	opt->lcd = 13000 * opt->l / k;
+}
+
+static void calculate_reg_values(struct md_opt *opt)
+{
+	int f = 0;		/* frequency change bit */
+	int turbo = 0;		/* turbo mode bit; depends on N value */
+
+	opt->regs.voltage = opt->v;
+/*
+  CCCR
+    | 31| 30|29-28| 27| 26| 25|24-11| 10| 9 | 8 | 7 |6-5  | 4 | 3 | 2 | 1 | 0 |
+    | C | P |     | L | P |   |     |	       |     |		   |
+    | P | P |     | C | L |   |     |	       |     |		   |
+    | D | D |resrv| D | L | A |resrv|  2 * N	|resrv|    L	      |
+    | I | I |     | 2 | . |   |     |	       |     |		   |
+    | S | S |     | 6 | . |   |     |	       |     |		   |
+
+    A: Alternate setting for MEMC clock
+       0 = MEM clock frequency as specified in YB's table 3-7
+       1 = MEM clock frq = System Bus Frequency
+
+
+  CLKCFG
+    | 31------------------------------------------- | 3 | 2  | 1 | 0 |
+    | --------------------------------------------- | B | HT | F | T |
+
+    B = Fast-Bus Mode  0: System Bus is half of run-mode
+		       1: System Bus is equal to run-mode
+		       NOTE: only allowed when L <= 16
+
+    HT = Half-Turbo    0: core frequency = run or turbo, depending on T bit
+		       1: core frequency = turbo frequency / 2
+		       NOTE: only allowed when 2N = 6 or 2N = 8
+
+    F = Frequency change
+		       0: No frequency change is performed
+		       1: Do frequency-change
+
+    T = Turbo Mode     0: CPU operates at run Frequency
+		       1: CPU operates at Turbo Frequency (when n2 > 2)
+
+*/
+	/* Set the CLKCFG with B, T, and HT */
+	if (opt->b != -1 && opt->n != -1) {
+		f = 1;
+
+		/*When N2=2, Turbo Mode equals Run Mode, so it
+		   does not really matter if this is >2 or >=2
+		 */
+		if (opt->n > 2) {
+			turbo = 0x1;
+		}
+		opt->regs.clkcfg = (opt->b << 3) + (f << 1) + turbo;
+	} else {
+		f = 0x1;
+		opt->regs.clkcfg = (f << 1);
+	}
+
+	/*
+	   What about when n2=0 ... it is not defined by the yellow
+	   book
+	 */
+	if (opt->n != -1) {
+		/* N2 is 4 bits, L is 5 bits */
+		opt->regs.cccr = ((opt->n & 0xF) << 7) + (opt->l & 0x1F);
+	}
+
+	if (opt->cccra > 0) {
+		/* Turn on the CCCR[A] bit */
+		opt->regs.cccr |= (1 << 25);
+	}
+
+	/* 13M Mode */
+	if (opt->l == 1) {
+	}
+
+	if ( (opt->l > 1) && (opt->cpll_enabled == 0) ) {
+	  	 printk(KERN_WARNING
+		  "DPM: internal error if l>1 CPLL must be On\n");
+	}
+	if( (opt->cpll_enabled == 1) && (opt->ppll_enabled == 0) ){
+	  	 printk(KERN_WARNING
+		 "DPM: internal error CPLL=On PPLL=Off is NOT supported in hardware\n");
+ 	}
+	if(opt->cpll_enabled == 0) {
+	 	 opt->regs.cccr |= (CCCR_CPDIS_BIT_ON);
+	}
+	if(opt->ppll_enabled == 0) {
+	 	 opt->regs.cccr |= (CCCR_PPDIS_BIT_ON);
+	}
+
+}
+
+/* This routine computes the "forward" frequency scaler flags
+ * for moving the system
+ * from the current operating point to the new operating point.  The resulting
+ * fscaler is applied to the registers of the new operating point.
+ */
+void compute_fscaler_flags(struct md_opt *cur, struct md_opt *new)
+{
+	int current_n, ccsr;
+
+	ccsr = CCSR;
+	current_n = (ccsr & CCCR_N_MASK) >> 7;
+	fscaler_flags = FSCALER_NOP;
+	/* If new CPU is 0, that means sleep, we do NOT switch PLLs
+	   if going to sleep.
+	 */
+	if (!new->cpu) {
+		if (new->sleep_mode == CPUMODE_DEEPSLEEP) {
+			fscaler_flags |= FSCALER_DEEPSLEEP;
+		} else if (new->sleep_mode == CPUMODE_STANDBY) {
+			fscaler_flags |= FSCALER_STANDBY;
+		} else {
+			fscaler_flags |= FSCALER_SLEEP;
+		}
+	} else {
+
+		/*
+		 * If exiting 13M mode, set the flag so we can do the extra
+		 * work to get out before the frequency change
+		 */
+		if( ((cur->cpll_enabled == 0) && (new->cpll_enabled ==1)) ||
+			((cur->ppll_enabled == 0) && (new->ppll_enabled ==1)) ){
+			fscaler_flags |= FSCALER_XPLLON;
+		}
+
+	}
+
+
+	/* if CPU is *something*, it means we are not going to sleep */
+	if ((new->cpu) &&
+	    /* And something has indeed changed */
+	    ((new->regs.cccr != cur->regs.cccr) ||
+	     (new->regs.clkcfg != cur->regs.clkcfg))) {
+
+		/* Find out if it is *just* a turbo bit change */
+		if ((cur->l == new->l) &&
+		    (cur->cccra == new->cccra) &&
+		    (cur->b == new->b) &&
+		    (cur->half_turbo == new->half_turbo)) {
+			/*
+			 * If the real, current N is a turbo freq and
+			 * the new N is not a turbo freq, then set
+			 * TURBO_OFF and do not change N
+			 */
+			if ((cur->n > 1) && (new->n == 2)) {
+				fscaler_flags |= FSCALER_TURBO_OFF;
+			}
+			/*
+			 * Else if the current operating point's N is
+			 * not-turbo and the new N is the desired
+			 * destination N, then set TURBO_ON
+			 */
+			else if ((cur->n == 2) && (new->n == current_n)) {
+				/*
+				 * Desired N must be what is current
+				 * set in the CCCR/CCSR
+				 */
+				fscaler_flags |= FSCALER_TURBO_ON;
+			}
+			/* Else, fall through to regular FCS     */
+		}
+		if (!(fscaler_flags & FSCALER_TURBO)) {
+			/* It this is not a Turbo bit only change, it
+			   must be a regular FCS
+			 */
+			fscaler_flags |= FSCALER_CPUFREQ;
+		}
+		loops_per_jiffy = new->lpj;
+	}
+
+	if (new->half_turbo != cur->half_turbo) {
+		loops_per_jiffy = new->lpj;
+
+		if (new->half_turbo)
+			fscaler_flags |= FSCALER_HALFTURBO_ON;
+		else
+			fscaler_flags |= FSCALER_HALFTURBO_OFF;
+	}
+
+	if (new->regs.voltage != cur->regs.voltage)
+		fscaler_flags |= FSCALER_VOLTAGE;
+
+}
+
+static int pxa27x_transition(struct powerop *cur, struct powerop *new)
+{
+	int rc = 0;
+	unsigned target_v;
+	struct md_opt *md_cur, *md_new;
+
+	pr_debug("%s: %s => %s\n", __FUNCTION__, cur->name, new->name);
+
+	md_cur = (struct md_opt *)cur->md_data;
+	md_new = (struct md_opt *)new->md_data;
+
+	/* fully define the new opt, if necessary, based on values
+	   from the current opt
+	 */
+	mainstone_fully_define_opt(cur, new);
+	target_v = md_new->v;
+
+	/* In accordance with Yellow Book section 3.7.6.3, "Coupling
+	   Voltage Change with Frequency Change", always set the
+	   voltage first (setting the FVC bit in the PCFR) and then do
+	   the frequency change
+	 */
+	rc = mainstone_fscale(cur, new);
+	if (rc == 0)
+		current_state = new;
+
+	return rc;
+}
+
+static int pxa27x_prepare_transition(struct powerop *cur, struct powerop *new)
+{
+	return powerop_driver_scale(SCALE_PRECHANGE, new);
+}
+
+static int mainstone_fscale(struct powerop *c, struct powerop *n)
+{
+	struct md_opt *cur = (struct md_opt *)c->md_data;
+	struct md_opt *new = (struct md_opt *)n->md_data;
+
+	compute_fscaler_flags(cur, new);
+
+	mainstone_fscaler(&new->regs);
+
+}
+
+static int pxa27x_finish_transition(struct powerop *cur, struct powerop *new)
+{
+
+	if ((new->type == PM_SUSPEND_MEM) || (new->type == PM_SUSPEND_DEEP)
+		|| (new->type == PM_SUSPEND_STANDBY)) {
+			/* If sleeping, keep the old LP2YJ,
+			 * so nothing to do here
+			 */
+	} else {
+		/* Normal change (not sleep), just compute. Always use
+		   the "baseline" lpj and freq */
+		powerop_driver_scale(SCALE_POSTCHANGE, new);
+	}
+
+	return 0;
+}
+
+void mainstone_fully_define_opt(struct powerop *c, struct powerop *n)
+{
+	struct md_opt *cur = (struct md_opt *)c->md_data;
+	struct md_opt *new = (struct md_opt *)n->md_data;
+
+	if (new->v == -1)
+		new->v = cur->v;
+	if (new->l == -1)
+		new->l = cur->l;
+	if (new->n == -1)
+		new->n = cur->n;
+	if (new->b == -1)
+		new->b = cur->b;
+	if (new->half_turbo == -1)
+		new->half_turbo = cur->half_turbo;
+	if (new->cccra == -1)
+		new->cccra = cur->cccra;
+	if (new->cpll_enabled == -1)
+		new->cpll_enabled = cur->cpll_enabled;
+	if (new->ppll_enabled == -1)
+		new->ppll_enabled = cur->ppll_enabled;
+	if (new->sleep_mode == -1)
+		new->sleep_mode = cur->sleep_mode;
+
+#ifdef CONFIG_BULVERDE_B0
+	/* for "B0"-revision PLLs have the same value */
+	new->ppll_enabled = new->cpll_enabled;
+#endif
+	/* PXA27x manual ("Yellow book") 3.5.5 (Table 3-7) states that
+	 * CPLL-"On" and PPLL-"Off"
+	 * configuration is forbidden (all others seem to be OK for "B0")
+	 * for "C0" boards we suppose that this configuration is also enabled.
+	 * PXA27x manual ("Yellow book") also states at 3.5.7.1 (page 3-25)
+	 * that "CCCR[PPDIS] and CCCR[CPDIS] must always be identical and
+	 * changed together". "If PLLs are to be turned off using xPDIS then
+	 * set xPDIS before frequency change and clear xPDIS after frequency
+	 * change"
+	 */
+
+	if (new->n > 2) {
+		new->turbo = 1;
+		/* turbo mode: 13K * L * (N/2)
+		   Shift at the end to divide N by 2 for Turbo mode or
+		   by 4 for Half-Turbo mode )
+		 */
+		new->cpu = (13000 * new->l * new->n) >>
+		    ((new->half_turbo == 1) ? 2 : 1);
+	} else {
+		new->turbo = 0;
+		/* run mode */
+		new->cpu = 13000 * new->l;
+	}
+	/* lcd freq is derived from L */
+	calculate_lcd_freq(new);
+	calculate_reg_values(new);
+	/* We want to keep a baseline loops_per_jiffy/cpu-freq ratio
+	   to work off of for future calculations, especially when
+	   emerging from sleep when there is no current cpu frequency
+	   to calculate from (because cpu-freq of 0 means sleep).
+	 */
+	if (!saved_loops_per_jiffy) {
+		saved_loops_per_jiffy = loops_per_jiffy;
+		saved_cpu_freq = cur->cpu;
+	}
+	if (!saved_cpu_freq) {
+		saved_cpu_freq = c->frequency;
+	}
+	/*
+	 * a dedicated method for updating jiffies when frequency is changed
+	 */
+	if (new->cpu) {
+		/* Normal change (not sleep), just compute. Always use
+		   the "baseline" lpj and freq */
+		new->lpj = powerop_compute_lpj(saved_loops_per_jiffy,
+		    saved_cpu_freq, new->cpu);
+	} else {
+		/* If sleeping, keep the old LPJ */
+		new->lpj = loops_per_jiffy;
+	}
+}
+
+static void xpll_on(struct powerop_regs *regs, int fscaler_flags)
+{
+	int tmp_cccr, tmp_ccsr;
+	int new_cpllon=0, new_pllon=0, cur_cpllon=0;
+	int  cur_pllon=0, start_cpll=0, start_pll=0;
+
+	tmp_ccsr = CCSR;
+
+	if ((regs->cccr & CCCR_CPDIS_BIT_ON) == 0)
+		new_cpllon=1;
+	if ((regs->cccr & CCCR_PPDIS_BIT_ON) == 0)
+		new_pllon=1;
+	if (((tmp_ccsr >> 31) & 0x1) == 0)
+		cur_cpllon=1;
+	if (((tmp_ccsr >> 30) & 0x1) == 0)
+		cur_pllon=1;
+
+	if ((new_cpllon == 1) && (cur_cpllon == 0)) {
+		start_cpll=1;
+	}
+	if ((new_pllon == 1) && (cur_pllon == 0)) {
+		start_pll=1;
+ 	}
+
+	if (!(fscaler_flags & FSCALER_XPLLON)) {
+		return;
+	}
+	if ((start_cpll == 0) && (start_pll == 0)) {
+		return;
+	}
+	/* NOTE: the Yellow Book says that exiting 13M mode requires a
+	   PLL relock, which takes at least 120uS, so the book suggests
+	   the OS could use a timer to keep busy until it is time to
+	   check the CCSR bits which must happen before changing the
+	   frequency back.
+
+	   For now, we'll just loop.
+	 */
+
+	/* From Yellow Book, page 3-31, section 3.5.7.5 13M Mode
+
+	   Exiting 13M Mode:
+
+	   1. Remain in 13M mode, but early enable the PLL via
+	   CCCR[CPDIS, PPDIS]=11, and CCCR[PLL_EARLY_EN]=1. Doing
+	   so will allow the PLL to be started early.
+
+	   2. Read CCCR and compare to make sure that the data was
+	   correctly written.
+
+	   3. Check to see if CCS[CPLOCK] and CCSR[PPLOCK] bits are
+	   both set. Once these bits are both high, the PLLs are
+	   locked and you may move on.
+
+	   4. Note that the CPU is still in 13M mode, but the PLLs are
+	   started.
+
+	   5. Exit from 13M mode by writing CCCR[CPDIS, PPDIS]=00, but
+	   maintain CCCR[PLL_EARLY_EN]=1. This bit will be cleared
+	   by the imminent frequency change.
+	 */
+
+	/* Step 1 */
+	tmp_cccr = CCCR;
+	if (start_cpll)
+		tmp_cccr |= CCCR_CPDIS_BIT_ON;
+	if (start_pll)
+		tmp_cccr |= CCCR_PPDIS_BIT_ON;
+	tmp_cccr |= CCCR_PLL_EARLY_EN_BIT_ON;
+
+	CCCR = tmp_cccr;
+
+	/* Step 2 */
+	tmp_cccr = CCCR;
+
+	if ((tmp_cccr & CCCR_PLL_EARLY_EN_BIT_ON) != CCCR_PLL_EARLY_EN_BIT_ON) {
+		printk(KERN_WARNING
+		       "DPM: Warning: PLL_EARLY_EN is NOT on\n");
+ 	}
+	if ((start_cpll==1) &&
+	      ((tmp_cccr & CCCR_CPDIS_BIT_ON) != CCCR_CPDIS_BIT_ON)) {
+		printk(KERN_WARNING
+		       "DPM: Warning: CPDIS is NOT on\n");
+	}
+	if ((start_pll==1) &&
+	      (tmp_cccr & CCCR_PPDIS_BIT_ON) != CCCR_PPDIS_BIT_ON) {
+		printk(KERN_WARNING
+		       "DPM: Warning: PPDIS is NOT on\n");
+	}
+
+	/* Step 3 */
+	{
+		/* Note: the point of this is to "wait" for the lock
+		   bits to be set; the Yellow Book says this may take
+		   a while, but observation indicates that it is
+		   instantaneous
+		 */
+
+		long volatile int i = 0;
+
+		int cpll_complete=1;
+		int pll_complete=1;
+		if (start_cpll==1)
+			cpll_complete=0;
+		if (start_pll==1)
+			pll_complete=0;
+
+		/*loop  arbitrary big value to prevent  looping forever */
+		for (i = 0; i < 999999; i++) {
+			tmp_ccsr = CCSR;
+
+			if ((tmp_ccsr & CCSR_CPLL_LOCKED) == CCSR_CPLL_LOCKED) {
+				/*CPLL locked*/
+				cpll_complete=1;
+			}
+			if ((tmp_ccsr & CCSR_PPLL_LOCKED) == CCSR_PPLL_LOCKED) {
+				/*PPLL locked*/
+				pll_complete=1;
+			}
+			if ((cpll_complete == 1) && (pll_complete == 1)) {
+				break;
+ 			}
+		}
+	}
+
+	/* Step 4: NOP */
+
+	/* Step 5
+	   Clear the PLL disable bits - do NOT do it here.
+	 */
+
+	/* But leave EARLY_EN on; it will be cleared by the frequency change */
+	regs->cccr |= CCCR_PLL_EARLY_EN_BIT_ON;
+	/* Do not set it now
+	   Step 6: Now go continue on with frequency change
+	   We do this step later as if voltage is too low,
+	   we must ensure that it rised up  before entereng to higher
+	   freq mode or simultaniously
+	 */
+}
+
+static void mainstone_fscaler(struct powerop_regs *regs)
+{
+	unsigned int cccr, clkcfg = 0;
+	unsigned long s;
+
+	/* If no flags are set, don't waste time here, just return */
+	if (fscaler_flags == FSCALER_NOP)
+		return;
+
+	if (!(fscaler_flags & FSCALER_ANY_SLEEPMODE))
+		local_irq_save(s);
+
+	/* If exiting 13M mode (turn on  PLL(s) ), do some extra work
+	   before changing the CPU frequency or voltage.
+	   We may turn on a combination of PLLs supported by hardware
+	   only. Otherwise xpll_on(...) hang the system.
+	 */
+	if (fscaler_flags & FSCALER_XPLLON)
+		xpll_on(regs, fscaler_flags);
+
+	/* if not sleeping, and have a voltage change
+	   note that SLEEPMODE will handle voltage itself
+	 */
+	if (((fscaler_flags & FSCALER_ANY_SLEEPMODE) == 0) &&
+	    (fscaler_flags & FSCALER_VOLTAGE)) {
+		if (fscaler_flags & FSCALER_CPUFREQ) {
+			/* coupled voltage & freq change */
+			mainstone_scale_voltage_coupled(regs);
+		} else {
+			/* Scale CPU voltage un-coupled with freq */
+			mainstone_scale_voltage(regs);
+		}
+	}
+
+	if (fscaler_flags & FSCALER_CPUFREQ)	/* Scale CPU freq */
+		mainstone_scale_cpufreq(regs);
+
+	if ((fscaler_flags & FSCALER_VOLTAGE) &&
+	    (fscaler_flags & FSCALER_CPUFREQ))
+		PCFR &= ~PCFR_FVC;
+
+	if (fscaler_flags & FSCALER_TURBO) {
+
+		clkcfg = mainstone_read_clkcfg();
+
+		/* Section 3.5.7 of the Yellow Book says that the F
+		   bit will be left on after a FCS, so we need to
+		   explicitly clear it. But do not change the B bit
+		 */
+		clkcfg &= ~(CLKCFG_F_BIT);
+
+		if (fscaler_flags & FSCALER_TURBO_ON) {
+			clkcfg = clkcfg | (CLKCFG_T_BIT);
+		} else {
+			clkcfg = clkcfg & ~(CLKCFG_T_BIT);
+		}
+
+		/* enable */
+		mainstone_set_freq(clkcfg);
+	}
+
+	if ((fscaler_flags & FSCALER_HALFTURBO_ON) ||
+	    (fscaler_flags & FSCALER_HALFTURBO_OFF)) {
+		if ((fscaler_flags & FSCALER_CPUFREQ) ||
+		    (fscaler_flags & FSCALER_VOLTAGE)) {
+
+			/*
+			   From the Yellow Book, p 3-106:
+
+			   "Any two writes to CLKCFG or PWRMODE
+			   registers must be separated by siz 13-MHz
+			   cycles.  This requirement is achieved by
+			   doing the write to the CLKCFG or POWERMODE
+			   reigster, performing a read of CCCR, and
+			   then comparing the value in the CLKCFG or
+			   POWERMODE register to the written value
+			   until it matches."
+
+			   Since the setting of half turbo is a
+			   separate write to CLKCFG, we need to adhere
+			   to this requirement.
+			 */
+			cccr = CCCR;
+			clkcfg = mainstone_read_clkcfg();
+			while (clkcfg != regs->clkcfg)
+				clkcfg = mainstone_read_clkcfg();
+		}
+
+		if (clkcfg == 0)
+			clkcfg = regs->clkcfg;
+		/* Turn off f-bit.
+
+		   According to the Yellow Book, page 3-23, "If only
+		   HT is set, F is clear, and B is not altered, then
+		   the core PLL is not stopped."
+		 */
+		clkcfg = clkcfg & ~(CLKCFG_F_BIT);
+		/* set half turbo bit */
+		if (fscaler_flags & FSCALER_HALFTURBO_ON) {
+			clkcfg = clkcfg | (CLKCFG_HT_BIT);
+		} else {
+			clkcfg = clkcfg & ~(CLKCFG_HT_BIT);
+		}
+
+		/* enable */
+		mainstone_set_freq(clkcfg);
+	}
+
+	/* Devices only need to scale on a core frequency
+	   change. Half-Turbo changes are separate from the regular
+	   frequency changes, so Half-Turbo changes do not need to
+	   trigger a device recalculation.
+
+	   NOTE: turbo-mode-only changes could someday also be
+	   optimized like Half-Turbo (to not trigger a device
+	   recalc).
+	 */
+
+	if (fscaler_flags & FSCALER_ANY_SLEEPMODE) {
+		/* NOTE: voltage needs i2c, so be sure to change
+		   voltage BEFORE* calling device_suspend
+		 */
+
+		if (fscaler_flags & FSCALER_VOLTAGE) {
+			/* Scale CPU voltage un-coupled with freq */
+			mainstone_scale_voltage(regs);
+		}
+
+		if (fscaler_flags & FSCALER_SLEEP) {
+			pm_suspend(&sleep);
+		} else if (fscaler_flags & FSCALER_STANDBY) {
+			pm_suspend(&standby);
+		} else if (fscaler_flags & FSCALER_DEEPSLEEP) {
+			pm_suspend(&deepsleep);
+		}
+
+		/* Here when we wake up. */
+
+	} else {
+		local_irq_restore(s);
+	}
+}
+
+/*
+ * Fully determine the current machine-dependent operating point, and fill in a
+ * structure presented by the caller.
+ */
+
+void mainstone_get_current_info(void)
+{
+	unsigned int tmp_cccr;
+	unsigned int cpdis;
+	unsigned int ppdis;
+	struct md_opt *opt = (struct md_opt *)current_state->md_data;
+
+	/* You should read CCSR to see what's up...but there is no A
+	   bit in the CCSR, so we'll grab it from the CCCR.
+	 */
+	tmp_cccr = CCCR;
+	opt->cccra = (tmp_cccr >> 25) & 0x1;	/* cccr[A]: bit 25 */
+
+	/* NOTE: the current voltage is not obtained, but will be left
+	   as 0 in the opt which will mean no voltage change at all
+	 */
+
+	opt->regs.cccr = CCSR;
+
+	opt->l = opt->regs.cccr & CCCR_L_MASK;	/* Get L */
+	opt->n = (opt->regs.cccr & CCCR_N_MASK) >> 7;	/* Get 2N */
+
+	/* This should never really be less than 2 */
+	if (opt->n < 2) {
+		opt->n = 2;
+	}
+
+	opt->regs.clkcfg = mainstone_read_clkcfg();
+	opt->b = (opt->regs.clkcfg >> 3) & 0x1;	/* Fast Bus (b): bit 3 */
+	opt->turbo = opt->regs.clkcfg & 0x1;	/* Turbo is bit 1 */
+	opt->half_turbo = (opt->regs.clkcfg >> 2) & 0x1;/* HalfTurbo: bit 2 */
+
+	calculate_lcd_freq(opt);
+
+	/* are any of the PLLs is on? */
+	cpdis = ((opt->regs.cccr >> 31) & 0x1);
+	ppdis = ((opt->regs.cccr >> 30) & 0x1);
+	/*
+	 * Newer revisions still require that if CPLL is On
+	 * then PPLL must also be On.
+	 */
+	if ((cpdis == 0) && (ppdis != 0)) {
+	  	/*
+		 * CPLL=On PPLL=Off is NOT supported with hardware.
+		 * NOTE:"B0"-revision has even more restrictive requirments
+		 * to PLLs
+		 */
+ 		printk("PowerOp: cpdis and ppdis are not in sync!\n");
+ 	}
+
+	opt->cpll_enabled = (cpdis == 0);
+	opt->ppll_enabled = (ppdis == 0);
+
+	/* Shift 1 to divide by 2 (because opt->n is really 2*N */
+	if (opt->turbo) {
+		opt->cpu = (13000 * opt->l * opt->n) >> 1;
+	} else {
+		/*
+		 * turbo bit is off, so skip N multiplier (no matter
+		 * what N really is) and use Run frequency (13K * L)
+		 */
+		opt->cpu = 13000 * opt->l;
+	}
+}
+
+static void powerop_print_opt(struct powerop *opt)
+{
+	struct md_opt *md_opt = (struct md_opt *)opt->md_data;
+
+	printk("PowerOP : Table of defined operating points:\n");
+	printk("\t%s desc %s freq %d volt %d latency %d\n", opt->name,
+	     opt->description, opt->frequency, opt->voltage, opt->latency);
+
+	printk("        Name  Vol   CPU    L    N    A    B   HT  PLL CPLL Sleep LCD\n");
+
+	printk("%12s %5d%5d%5d%5d%5d%5d%5d%5d%5d%5d%5d\n",
+	    opt->name, (md_opt->v), (md_opt->cpu / 1000), md_opt->l, md_opt->n,
+	    md_opt->cccra, md_opt->b, md_opt->half_turbo, md_opt->cpll_enabled,
+	    md_opt->ppll_enabled, md_opt->sleep_mode, (md_opt->lcd / 1000));
+	return ;
+}
+
+/* Crystal clock: 13MHz */
+#define BASE_CLK  13000000
+
+int __init powerop_mainstone_init(void)
+{
+	unsigned int freq;
+	unsigned long ccsr;
+	unsigned int l;
+
+	printk("Mainstone PowerOP Power Management\n");
+
+	mainstone_clk_init();
+	mainstone_vcs_init();
+        ccsr = CCSR;
+        l  = ccsr & 0x1f;
+        freq  = l * BASE_CLK;
+
+	printk("POWEROP: got frequency %d\n", freq);
+	/*
+	 * supported operating point sets.
+	 * 104, 208, 312
+	 * 208, 312, 416, 520
+	 */
+	switch (freq) {
+	    case 208000000: {
+		mainstone_setup_opt(&low, 104, &mhz104);
+		powerop_print_opt(&low);
+		mainstone_setup_opt(&medium, 208, &mhz208);
+		powerop_print_opt(&medium);
+		mainstone_setup_opt(&high, 312, &mhz312);
+		powerop_print_opt(&high);
+		current_state = &medium;
+		break;
+	    }
+	    case 520000000: {
+		mainstone_setup_opt(&low, 208, &mhz208hi);
+		powerop_print_opt(&low);
+		mainstone_setup_opt(&medium, 312, &mhz312hi);
+		powerop_print_opt(&medium);
+		mainstone_setup_opt(&high, 416, &mhz416hi);
+		powerop_print_opt(&high);
+		mainstone_setup_opt(&highest, 520, &mhz520hi);
+		powerop_print_opt(&highest);
+		break;
+
+	    }
+	    default: {
+		printk("PowerOp: unknown frequency set %d\n", freq);
+		break;
+	    }
+	}
+
+        if (lowest.frequency)
+                list_add_tail(&lowest.list, &pm_states.list);
+        if (low.frequency)
+                list_add_tail(&low.list, &pm_states.list);
+        if (mediumlow.frequency)
+                list_add_tail(&mediumlow.list, &pm_states.list);
+        if (medium.frequency)
+                list_add_tail(&medium.list, &pm_states.list);
+        if (mediumhigh.frequency)
+                list_add_tail(&mediumhigh.list, &pm_states.list);
+        if (high.frequency) {
+                list_add_tail(&high.list, &pm_states.list);
+        }
+        if (highest.frequency) {
+                list_add_tail(&highest.list, &pm_states.list);
+        }
+	/*
+	 * add sleep states
+	 */
+	mainstone_setup_opt(&sleep, 0, &md_sleep);
+	powerop_print_opt(&sleep);
+	mainstone_setup_opt(&deepsleep, 0, &md_deepsleep);
+	powerop_print_opt(&deepsleep);
+	mainstone_setup_opt(&standby, 0, &md_standby);
+	powerop_print_opt(&standby);
+	list_add_tail(&sleep.list, &pm_states.list);
+	list_add_tail(&deepsleep.list, &pm_states.list);
+	list_add_tail(&standby.list, &pm_states.list);
+
+	return 0;
+}
+
+void __exit powerop_mainstone_exit(void) {
+	mainstone_freq_cleanup();
+	mainstone_voltage_cleanup();
+}
+
+__initcall(powerop_mainstone_init);
+__exitcall(powerop_mainstone_exit);
Index: linux-2.6.17/arch/arm/mach-pxa/mainstone_volt.c
===================================================================
--- /dev/null
+++ linux-2.6.17/arch/arm/mach-pxa/mainstone_volt.c
@@ -0,0 +1,364 @@
+/*
+ * linux/arch/arm/mach-pxa/mainstone_voltage.c
+ *
+ * Bulverde voltage change driver.
+ *
+ * Author: <source@mvista.com>
+ *
+ * 2003, 2006 (c) MontaVista Software, Inc. This file is licensed under the
+ * terms of the GNU General Public License version 2. This program is
+ * licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <asm/hardware.h>
+#include <asm/arch/powerop.h>
+#include <asm/arch/pxa-regs.h>
+
+/* For ioremap */
+#include <asm/io.h>
+
+#define CP15R0_REV_MASK		0x0000000f
+#define PXA270_C5		0x7
+
+static u32 chiprev;
+static unsigned int blvd_min_vol, blvd_max_vol;
+static int mvdt_size;
+
+static volatile int *ramstart;
+
+struct MvDAC {
+	unsigned int mv;
+	unsigned int DACIn;
+} *mvDACtable;
+
+/*
+ *  Transfer desired mv to required DAC value.
+ *  Vcore = 1.3v - ( 712uv * DACIn )
+ */
+static struct MvDAC table_c0[] = {
+	{1425, 0},
+	{1400, 69},
+	{1300, 248},
+	{1200, 428},
+	{1100, 601},
+	{1000, 777},
+	{950, 872},
+	{868, 1010},
+	{861, 0xFFFFFFFF},
+};
+
+/*
+ *  Transfer desired mv to required DAC value, update for new boards,
+ *  according to "Intel PXA27x Processor Developer's Kit User's Guide,
+ *  April 2004, Revision 4.001"
+ *  Vcore = 1.5V - (587uV * DAC(input)).
+ */
+static struct MvDAC table_c5[] = {
+	{1500, 0},
+	{1484,25},
+	{1471,50},
+	{1456,75},
+	{1441,100},
+	{1427,125},
+	{1412,150},
+	{1397,175},
+	{1383,200},
+	{1368,225},
+	{1353,250},
+	{1339,275},
+	{1323,300},
+	{1309,325},
+	{1294,350},
+	{1280,375},
+	{1265,400},
+	{1251,425},
+	{1236,450},
+	{1221,475},
+	{1207,500},
+	{1192,525},
+	{1177,550},
+	{1162,575},
+	{1148,600},
+	{1133,625},
+	{1118,650},
+	{1104,675},
+	{1089,700},
+	{1074,725},
+	{1060,750},
+	{1045,775},
+	{1030,800},
+	{1016,825},
+	{1001,850},
+	{986,875},
+	{972,900},
+	{957,925},
+	{942,950},
+	{928,975},
+	{913,1000},
+	{899, 1023},
+};
+
+unsigned int mainstone_validate_voltage(unsigned int mv)
+{
+	/*
+	 *      Just to check whether user specified mv
+	 *      can be set to the CPU.
+	 */
+	if ((mv >= blvd_min_vol) && (mv <= blvd_max_vol)) {
+		return mv;
+	} else {
+		return 0;
+	}
+}
+
+/*
+ * Prepare for a voltage change, possibly coupled with a frequency
+ * change
+ */
+static void power_change_cmd(unsigned int DACValue, int coupled);
+void mainstone_prep_set_voltage(unsigned int mv)
+{
+        power_change_cmd(mv2DAC(mv), 1 /* coupled */ );
+}
+
+
+unsigned int mv2DAC(unsigned int mv)
+{
+	int i, num = mvdt_size;
+
+	if (mvDACtable[0].mv <= mv) {	/* Max or bigger */
+		/* Return the first one */
+		return mvDACtable[0].DACIn;
+	}
+
+	if (mvDACtable[num - 1].mv >= mv) {	/* Min or smaller */
+		/* Return the last one */
+		return mvDACtable[num - 1].DACIn;
+	}
+
+	/*
+	 * The biggest and smallest value cases are covered, now the
+	 *  loop may skip those
+	 */
+	for (i = 1; i <= (num - 1); i++) {
+		if ((mvDACtable[i].mv >= mv) && (mvDACtable[i + 1].mv < mv)) {
+			return mvDACtable[i].DACIn;
+		}
+	}
+
+	/* Should never get here */
+	return 0;
+}
+extern void mainstone_change_voltage(void);
+void vm_setvoltage(unsigned int DACValue)
+{
+	power_change_cmd(DACValue, 0 /* not-coupled */ );
+	/* Execute voltage change sequence      */
+	mainstone_change_voltage();	/* set VC on the PWRMODE on CP14 */
+}
+/*
+ *	According to bulverde's manual, set the core's voltage.
+ */
+void mainstone_set_voltage(unsigned int mv)
+{
+	vm_setvoltage(mv2DAC(mv));
+}
+
+/*
+ *	Functionality: Initialize PWR I2C.
+ *	Argument:      None
+ *	Return:        void
+*/
+int mainstone_vcs_init(void)
+{
+	/*
+	 * we distinguish new and old boards by proc chip
+	 * revision, we assume new boards have C5 proc
+	 * revision and we use the new table (table_c5) for them,
+	 * for all other boards we use the old table (table_c0).
+	 * Note, the logics won't work and inaccurate voltage
+	 * will be set if C5 proc installed to old board
+	 * and vice versa.
+	 */
+
+	asm("mrc%? p15, 0, %0, c0, c0" : "=r" (chiprev));
+
+	chiprev &= CP15R0_REV_MASK;
+
+	if ( chiprev == PXA270_C5 ) {
+		mvDACtable = table_c5;
+		mvdt_size = sizeof(table_c5) / sizeof(struct MvDAC);
+		blvd_min_vol = BLVD_MIN_VOL_C5;
+		blvd_max_vol = BLVD_MAX_VOL_C5;
+	} else {
+		mvDACtable = table_c0;
+		mvdt_size = sizeof(table_c0) / sizeof(struct MvDAC);
+		blvd_min_vol = BLVD_MIN_VOL_C0;
+		blvd_max_vol = BLVD_MAX_VOL_C0;
+	}
+
+	CKEN |= 0x1 << 15;
+	CKEN |= 0x1 << 14;
+	PCFR |= 0x60;
+
+	/* map the first page of sdram to an uncached virtual page */
+	ramstart = (int *)ioremap(PHYS_OFFSET, 4096);
+
+	printk(KERN_INFO "CPU voltage change initialized.\n");
+
+	return 0;
+}
+
+void mainstone_voltage_cleanup(void)
+{
+	/* unmap the page we used*/
+	iounmap((void *)ramstart);
+}
+
+
+void mainstone_change_voltage(void)
+{
+	unsigned long flags;
+	unsigned int unused;
+
+
+	local_irq_save(flags);
+
+	__asm__ __volatile__("\n\
+    @ BV B0 WORKAROUND - Core hangs on voltage change at different\n\
+    @ alignments and at different core clock frequencies\n\
+    @ To ensure that no external fetches occur, we want to store the next\n\
+    @ several instructions that occur after the voltage change inside\n\
+    @ the cache. The load dependency stall near the retry label ensures \n\
+    @ that any outstanding instruction cacheline loads are complete before \n\
+    @ the mcr instruction is executed on the 2nd pass. This procedure \n\
+    @ ensures us that the internal bus will not be busy. \n\
+					\n\
+    b	    2f				\n\
+    nop					\n\
+    .align  5				\n\
+2:					\n\
+    ldr     r0, [%1]			@ APB register read and compare \n\
+    cmp     r0, #0			@ fence for pending slow apb reads \n\
+					\n\
+    mov     r0, #8			@ VC bit for PWRMODE \n\
+    movs    r1, #1			@ don't execute mcr on 1st pass \n\
+					\n\
+    @ %1 points to uncacheable memory to force memory read \n\
+					\n\
+retry:					\n\
+    ldreq   r3, [%2]			@ only stall on the 2nd pass\n\
+    cmpeq   r3, #0			@ cmp causes fence on mem transfers\n\
+    cmp     r1, #0			@ is this the 2nd pass? \n\
+    mcreq   p14, 0, r0, c7, c0, 0	@ write to PWRMODE on 2nd pass only \n\
+					\n\
+    @ Read VC bit until it is 0, indicates that the VoltageChange is done.\n\
+    @ On first pass, we never set the VC bit, so it will be clear already.\n\
+					\n\
+VoltageChange_loop:			\n\
+    mrc     p14, 0, r3, c7, c0, 0	\n\
+    tst     r3, #0x8			\n\
+    bne     VoltageChange_loop		\n\
+					\n\
+    subs    r1, r1, #1		@ update conditional execution counter\n\
+    beq     retry":"=&r"(unused)
+			     :"r"(&CCCR), "r"(ramstart)
+			     :"r0", "r1", "r3");
+
+	local_irq_restore(flags);
+
+}
+
+static void clr_all_sqc(void)
+{
+	int i = 0;
+	for (i = 0; i < 32; i++)
+		PCMD(i) &= ~PCMD_SQC;
+}
+
+static void clr_all_mbc(void)
+{
+	int i = 0;
+	for (i = 0; i < 32; i++)
+		PCMD(i) &= ~PCMD_MBC;
+}
+
+static void clr_all_dce(void)
+{
+	int i = 0;
+	for (i = 0; i < 32; i++)
+		PCMD(i) &= ~PCMD_DCE;
+}
+
+static void set_mbc_bit(int ReadPointer, int NumOfBytes)
+{
+	PCMD0 |= PCMD_MBC;
+	PCMD1 |= PCMD_MBC;
+}
+
+static void set_lc_bit(int ReadPointer, int NumOfBytes)
+{
+	PCMD0 |= PCMD_LC;
+	PCMD1 |= PCMD_LC;
+	PCMD2 |= PCMD_LC;
+}
+
+static void set_cmd_data(unsigned char *DataArray, int StartPoint, int size)
+{
+	PCMD0 &= 0xFFFFFF00;
+	PCMD0 |= DataArray[0];
+	PCMD1 &= 0xFFFFFF00;
+	PCMD1 |= DataArray[1];
+	PCMD2 &= 0xFFFFFF00;
+	PCMD2 |= DataArray[2];
+}
+
+/* coupled indicates that this VCS is to be coupled with a FCS */
+static void power_change_cmd(unsigned int DACValue, int coupled)
+{
+	unsigned char dataArray[3];
+
+	dataArray[0] = 0;				/* Command 0 */
+	dataArray[1] = (DACValue & 0x000000FF);		/* data LSB */
+	dataArray[2] = (DACValue & 0x0000FF00) >> 8;	/* data MSB */
+
+	PVCR = 0;
+
+	PCFR &= ~PCFR_FVC;
+	PVCR &= 0xFFFFF07F;	/*      no delay is necessary   */
+	PVCR &= 0xFFFFFF80;	/*  clear slave address         */
+	PVCR |= 0x20;		/*      set slave address       */
+
+	PVCR &= 0xFE0FFFFF;	/*      clear read pointer 0    */
+	PVCR |= 0;
+
+	/*  DCE and SQC are not necessary for single command */
+	clr_all_sqc();
+	clr_all_dce();
+
+	clr_all_mbc();
+	set_mbc_bit(0, 2);
+
+	/*  indicate the last byte of this command is holded in this register */
+	PCMD2 &= ~PCMD_MBC;
+
+	/*  indicate this is the first command and last command also        */
+	set_lc_bit(0, 3);
+
+	/*  programming the command data bit        */
+	set_cmd_data(dataArray, 0, 2);
+
+	/* Enable Power I2C */
+	PCFR |= PCFR_PI2CEN;
+
+	if (coupled) {
+		/* Enable Power I2C and FVC */
+		PCFR |= PCFR_FVC;
+	}
+}
Index: linux-2.6.17/include/asm-arm/arch-pxa/powerop.h
===================================================================
--- /dev/null
+++ linux-2.6.17/include/asm-arm/arch-pxa/powerop.h
@@ -0,0 +1,119 @@
+/*
+ * include/asm-arm/arch-pxa/powerop.h
+ *
+ * Bulverde-specific definitions for PowerOp.  If further PXA boards are
+ * supported in the future, will split into board-specific files.
+ *
+ * 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
+ *
+ * Copyright (C) 2002, 2006 MontaVista Software <source@mvista.com>
+ *
+ * Based on arch/ppc/platforms/ibm405lp_dpm.h by Bishop Brock.
+ */
+
+#ifndef __ASM_ARM_PXA_POWEROP_H__
+#define __ASM_ARM_PXA_POWEROP_H__
+
+#ifdef __KERNEL__
+#ifndef __ASSEMBLER__
+#include <linux/config.h>
+#include <linux/notifier.h>
+#include <linux/ioctl.h>
+#include <linux/miscdevice.h>
+
+#define CCLKCFG_TURBO   0x1
+#define CCLKCFG_FCS     0x2
+
+#define L_NUM   31              /*  30 different L numbers. */
+#define N_NUM   7               /*  7 N numbers. */
+
+#define BLVD_MIN_FREQ       13000
+/* latest PowerPoint documentation indicates 624000, not 540000*/
+#define BLVD_MAX_FREQ       624000
+
+void mainstone_set_freq(unsigned int);
+
+int mainstone_clk_init(void);
+unsigned int mainstone_read_clkcfg(void);
+void mainstone_freq_cleanup(void);
+
+#define BLVD_MAX_VOL_C5        1500	/*  in mV.  */
+#define BLVD_MIN_VOL_C5        899	/*  in Mv.  */
+#define BLVD_MAX_VOL_C0        1400	/*  in mV.  */
+#define BLVD_MIN_VOL_C0        850	/*  in Mv.  */
+
+unsigned int mv2DAC(unsigned int mv);
+void vm_setvoltage(unsigned int);
+unsigned int mainstone_validate_voltage(unsigned int mv);
+
+void mainstone_set_voltage(unsigned int mv);
+void mainstone_prep_set_voltage(unsigned int mv);
+int mainstone_vcs_init(void);
+
+void mainstone_set_freq(unsigned int);
+
+int mainstone_clk_init(void);
+unsigned int mainstone_read_clkcfg(void);
+void mainstone_freq_cleanup(void);
+
+enum {
+	CPUMODE_RUN,
+	CPUMODE_IDLE,
+	CPUMODE_STANDBY,
+	CPUMODE_SLEEP,
+	CPUMODE_RESERVED,
+	CPUMODE_SENSE,
+	CPUMODE_RESERVED2,
+	CPUMODE_DEEPSLEEP,
+};
+
+#define PM_SUSPEND_DEEP	((__force suspend_state_t) 2)
+
+struct powerop_regs {
+	unsigned int	cccr;
+	unsigned int	clkcfg;
+	unsigned int	voltage;	/*This is not a register.*/
+};
+
+/*
+ * Instances of this structure define valid Bulverde operating points for DPM.
+ * Voltages are represented in mV, and frequencies are represented in KHz.
+ */
+
+struct md_opt {
+       /* Specified values */
+        int v;         /* Target voltage in mV*/
+        int l;         /* Run Mode to Oscillator ratio */
+        int n;         /* Turbo-Mode to Run-Mode ratio */
+        int b;         /* Fast Bus Mode */
+        int half_turbo;/* Half Turbo bit */
+        int cccra;     /* the 'A' bit of the CCCR register,
+			  alternate MEMC clock */
+	int cpll_enabled; /* core PLL is ON?  (Bulverde >="C0" feature)*/
+	int ppll_enabled; /* peripherial PLL is ON? (Bulverde >="C0" feature)*/
+
+	int sleep_mode;
+        /*Calculated values*/
+	unsigned int lcd;	/*in KHz  */
+	unsigned int lpj;	/*New value for loops_per_jiffy */
+	unsigned int cpu;	/*CPU frequency in KHz */
+	unsigned int turbo;     /* Turbo bit in clkcfg */
+
+	struct powerop_regs regs;   /* Register values */
+};
+
+#endif /* __ASSEMBLER__ */
+#endif /* __KERNEL__ */
+#endif
Index: linux-2.6.17/include/asm-arm/arch-pxa/pxa-regs.h
===================================================================
--- linux-2.6.17.orig/include/asm-arm/arch-pxa/pxa-regs.h
+++ linux-2.6.17/include/asm-arm/arch-pxa/pxa-regs.h
@@ -13,7 +13,6 @@
 #ifndef __PXA_REGS_H
 #define __PXA_REGS_H
 
-
 /*
  * PXA Chip selects
  */
@@ -25,7 +24,6 @@
 #define PXA_CS4_PHYS	0x10000000
 #define PXA_CS5_PHYS	0x14000000
 
-
 /*
  * Personal Computer Memory Card International Association (PCMCIA) sockets
  */
@@ -64,8 +62,6 @@
 #define _PCMCIA1Attr	_PCMCIAAttr (1)	/* PCMCIA 1 Attribute              */
 #define _PCMCIA1Mem	_PCMCIAMem (1)	/* PCMCIA 1 Memory                 */
 
-
-
 /*
  * DMA Controller
  */
@@ -2042,6 +2038,14 @@
 #define MDMRS		__REG(0x48000040)  /* MRS value to be written to SDRAM */
 #define BOOT_DEF	__REG(0x48000044)  /* Read-Only Boot-Time Register. Contains BOOT_SEL and PKG_SEL */
 
+#define MDREFR_DRI 0xFFF
+#define MSC0_RDF (0xF << 20)
+#define MSC0_RDN (0xF << 24)
+#define MSC0_RRR (0x7 << 12)
+#define MDREFR_RFU 0xC0200000
+#define MDCNFG_DTC0 (0x3 << 8)
+#define MDCNFG_DTC2 (0x3 << 24)
+
 /*
  * More handy macros for PCMCIA
  *

[-- Attachment #3: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Dynanic On-The-Fly Operating points for PowerOP
  2006-08-12  8:07       ` Vitaly Wool
  2006-08-12 18:12         ` david singleton
  2006-08-12 21:32         ` david singleton
@ 2006-08-12 21:39         ` david singleton
  2006-08-12 21:40         ` david singleton
                           ` (2 subsequent siblings)
  5 siblings, 0 replies; 15+ messages in thread
From: david singleton @ 2006-08-12 21:39 UTC (permalink / raw)
  To: Vitaly Wool; +Cc: linux-pm

[-- Attachment #1: Type: text/plain, Size: 299 bytes --]

Here's the Powerop-core patch (again).  It hasn't changed much since 
the 2.6.18-rc1 version.
It's rolled forward to 2.6.18-rc4 and incorporated suggestions.  The 
/sys/power/supported_states
file only shows the names of the supported states instead of 
additional, table-like, information.

David


[-- Attachment #2: powerop-core.patch --]
[-- Type: application/octet-stream, Size: 26773 bytes --]


Signed-Off-by: David Singleton <dsingleton@mvista.com>

 Documentation/power/powerop.txt |  212 ++++++++++++++++++++++++++++++++++++++++
 drivers/base/core.c             |    5 
 drivers/base/driver.c           |    1 
 drivers/base/power/Makefile     |    2 
 drivers/base/power/powerop.c    |  103 +++++++++++++++++++
 drivers/base/power/resume.c     |    3 
 drivers/base/power/suspend.c    |    2 
 include/linux/device.h          |    1 
 include/linux/pm.h              |   56 ++++++++++
 kernel/power/main.c             |  189 ++++++++++++++++++++++++++++-------
 kernel/power/power.h            |    2 
 11 files changed, 532 insertions(+), 44 deletions(-)

Index: linux-2.6.17/kernel/power/main.c
===================================================================
--- linux-2.6.17.orig/kernel/power/main.c
+++ linux-2.6.17/kernel/power/main.c
@@ -49,7 +49,7 @@ void pm_set_ops(struct pm_ops * ops)
  *	the platform can enter the requested state.
  */
 
-static int suspend_prepare(suspend_state_t state)
+static int suspend_prepare(struct powerop * state)
 {
 	int error = 0;
 	unsigned int free_pages;
@@ -82,7 +82,7 @@ static int suspend_prepare(suspend_state
 	}
 
 	if (pm_ops->prepare) {
-		if ((error = pm_ops->prepare(state)))
+		if ((error = pm_ops->prepare(state->type)))
 			goto Thaw;
 	}
 
@@ -94,7 +94,7 @@ static int suspend_prepare(suspend_state
 	return 0;
  Finish:
 	if (pm_ops->finish)
-		pm_ops->finish(state);
+		pm_ops->finish(state->type);
  Thaw:
 	thaw_processes();
  Enable_cpu:
@@ -104,7 +104,7 @@ static int suspend_prepare(suspend_state
 }
 
 
-int suspend_enter(suspend_state_t state)
+int suspend_enter(struct powerop * state)
 {
 	int error = 0;
 	unsigned long flags;
@@ -115,7 +115,7 @@ int suspend_enter(suspend_state_t state)
 		printk(KERN_ERR "Some devices failed to power down\n");
 		goto Done;
 	}
-	error = pm_ops->enter(state);
+	error = pm_ops->enter(state->type);
 	device_power_up();
  Done:
 	local_irq_restore(flags);
@@ -131,36 +131,99 @@ int suspend_enter(suspend_state_t state)
  *	console that we've allocated. This is not called for suspend-to-disk.
  */
 
-static void suspend_finish(suspend_state_t state)
+static void suspend_finish(struct powerop * state)
 {
 	device_resume();
 	resume_console();
 	thaw_processes();
 	enable_nonboot_cpus();
 	if (pm_ops && pm_ops->finish)
-		pm_ops->finish(state);
+		pm_ops->finish(state->type);
 	pm_restore_console();
 }
 
 
+struct powerop *current_state;
+struct powerop pm_states = {
+	.name = "default",
+	.type = PM_SUSPEND_ON,
+};
+
+static struct powerop standby = {
+	.name = "standby",
+	.description = "Power-On Suspend ACPI State: S1",
+	.type = PM_SUSPEND_STANDBY,
+};
+struct powerop *powerop_standby;
 
+static struct powerop mem = {
+	.name = "mem",
+	.description = "Suspend-to-RAM ACPI State: S3",
+	.type = PM_SUSPEND_MEM,
+};
+struct powerop *powerop_mem;
 
-static const char * const pm_states[PM_SUSPEND_MAX] = {
-	[PM_SUSPEND_STANDBY]	= "standby",
-	[PM_SUSPEND_MEM]	= "mem",
 #ifdef CONFIG_SOFTWARE_SUSPEND
-	[PM_SUSPEND_DISK]	= "disk",
-#endif
+struct powerop disk = {
+	.name = "disk",
+	.description = "Suspend-to-disk ACPI State: S4",
+	.type = PM_SUSPEND_DISK,
 };
+struct powerop *powerop_disk;
+#endif
 
-static inline int valid_state(suspend_state_t state)
+/*
+ *
+ */
+static int pm_change_state(struct powerop *state)
+{
+	int error = -EINVAL;
+	int len = strlen(state->name);
+	struct powerop *this, *next;
+	struct list_head *head = &pm_states.list;
+
+	/*
+	 * list_find new operating point.
+	 * compare to current operating point.
+	 * if different change to new operating point.
+	 */
+	list_for_each_entry_safe(this, next, head, list) {
+		if (strncmp(state->name, this->name, len) == 0) {
+			if ((strcmp(current_state->name, this->name)) == 0) {
+				return 0;
+			}
+
+			if (this->prepare_transition(current_state, this)) {
+				break;
+			}
+
+			if (this->transition(current_state, this)) {
+				break;
+			}
+
+			/*
+			 * now lets wait for the transition latency
+			 */
+			udelay(this->latency);
+
+			error = this->finish_transition(current_state, this);
+
+			if (error == 0)
+				current_state = this;
+			break;
+		}
+	}
+	return error;
+}
+
+static inline int valid_state(struct powerop * state)
 {
 	/* Suspend-to-disk does not really need low-level support.
 	 * It can work with reboot if needed. */
-	if (state == PM_SUSPEND_DISK)
+	if (state->type == PM_SUSPEND_DISK)
 		return 1;
 
-	if (pm_ops && pm_ops->valid && !pm_ops->valid(state))
+	if (pm_ops && pm_ops->valid && !pm_ops->valid(state->type))
 		return 0;
 	return 1;
 }
@@ -168,7 +231,7 @@ static inline int valid_state(suspend_st
 
 /**
  *	enter_state - Do common work of entering low-power state.
- *	@state:		pm_state structure for state we're entering.
+ *	@state:		powerop structure for state we're entering.
  *
  *	Make sure we're the only ones trying to enter a sleep state. Fail
  *	if someone has beat us to it, since we don't want anything weird to
@@ -177,7 +240,7 @@ static inline int valid_state(suspend_st
  *	we've woken up).
  */
 
-static int enter_state(suspend_state_t state)
+static int enter_state(struct powerop *state)
 {
 	int error;
 
@@ -186,16 +249,21 @@ static int enter_state(suspend_state_t s
 	if (down_trylock(&pm_sem))
 		return -EBUSY;
 
-	if (state == PM_SUSPEND_DISK) {
+	if (state->type == PM_SUSPEND_DISK) {
 		error = pm_suspend_disk();
 		goto Unlock;
 	}
 
-	pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
+	if (state->type == PM_FREQ_CHANGE) {
+		error = pm_change_state(state);
+		goto Unlock;
+	}
+
+	pr_debug("PM: Preparing system for %s sleep\n", state->name);
 	if ((error = suspend_prepare(state)))
 		goto Unlock;
 
-	pr_debug("PM: Entering %s sleep\n", pm_states[state]);
+	pr_debug("PM: Entering %s sleep\n", state->name);
 	error = suspend_enter(state);
 
 	pr_debug("PM: Finishing wakeup.\n");
@@ -211,7 +279,15 @@ static int enter_state(suspend_state_t s
  */
 int software_suspend(void)
 {
-	return enter_state(PM_SUSPEND_DISK);
+	struct powerop *this, *next;
+	struct list_head *head = &pm_states.list;
+	int error = 0;
+
+	list_for_each_entry_safe(this, next, head, list) {
+		if (this->type == PM_SUSPEND_DISK)
+			error= enter_state(this);
+	}
+	return error;
 }
 
 
@@ -223,16 +299,44 @@ int software_suspend(void)
  *	structure, and enter (above).
  */
 
-int pm_suspend(suspend_state_t state)
+int pm_suspend(struct powerop * state)
 {
-	if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)
+	if (state->type > PM_SUSPEND_ON && state->type <= PM_SUSPEND_MAX)
 		return enter_state(state);
 	return -EINVAL;
 }
 
+decl_subsys(power,NULL,NULL);
 
+/**
+ *	supported_states - control system power state.
+ *
+ *	show() returns what states are supported, which are no longer
+ * 	hard-coded to just 'standby' (Power-On Suspend), 'mem' (Suspend-to-RAM),
+ *	and *'disk' (Suspend-to-Disk), but show all the power states.
+ *
+ *	store() unwritable
+ */
 
-decl_subsys(power,NULL,NULL);
+static ssize_t supported_states_show(struct subsystem * subsys, char * buf)
+{
+	struct powerop *this, *next;
+	struct list_head *head = &pm_states.list;
+	char * s = buf;
+
+	list_for_each_entry_safe(this, next, head, list) {
+		s += sprintf(s,"%s\n", this->name);
+	}
+
+	return (s - buf);
+}
+
+static ssize_t supported_states_store(struct subsystem * subsys, const char *buf, size_t n)
+{
+	return -EINVAL;
+}
+
+power_attr(supported_states);
 
 
 /**
@@ -248,36 +352,28 @@ decl_subsys(power,NULL,NULL);
 
 static ssize_t state_show(struct subsystem * subsys, char * buf)
 {
-	int i;
 	char * s = buf;
 
-	for (i = 0; i < PM_SUSPEND_MAX; i++) {
-		if (pm_states[i] && valid_state(i))
-			s += sprintf(s,"%s ", pm_states[i]);
-	}
-	s += sprintf(s,"\n");
+	s += sprintf(s,"%s\n", current_state->name);
 	return (s - buf);
 }
 
 static ssize_t state_store(struct subsystem * subsys, const char * buf, size_t n)
 {
-	suspend_state_t state = PM_SUSPEND_STANDBY;
-	const char * const *s;
+	struct powerop *this, *next;
+	struct list_head *head = &pm_states.list;
 	char *p;
-	int error;
+	int error = -EINVAL;
 	int len;
 
 	p = memchr(buf, '\n', n);
 	len = p ? p - buf : n;
-
-	for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
-		if (*s && !strncmp(buf, *s, len))
+	list_for_each_entry_safe(this, next, head, list) {
+		if (!strncmp(buf, this->name, len)) {
+			error = enter_state(this);
 			break;
+		}
 	}
-	if (state < PM_SUSPEND_MAX && *s)
-		error = enter_state(state);
-	else
-		error = -EINVAL;
 	return error ? error : n;
 }
 
@@ -285,6 +381,7 @@ power_attr(state);
 
 static struct attribute * g[] = {
 	&state_attr.attr,
+	&supported_states_attr.attr,
 	NULL,
 };
 
@@ -295,9 +392,21 @@ static struct attribute_group attr_group
 
 static int __init pm_init(void)
 {
+
 	int error = subsystem_register(&power_subsys);
 	if (!error)
 		error = sysfs_create_group(&power_subsys.kset.kobj,&attr_group);
+
+	INIT_LIST_HEAD(&pm_states.list);
+
+#ifdef CONFIG_SOFTWARE_SUSPEND
+	list_add(&disk.list, &pm_states.list);
+	powerop_disk = &disk;
+#endif
+	list_add(&mem.list, &pm_states.list);
+	list_add(&standby.list, &pm_states.list);
+	current_state = &pm_states;
+
 	return error;
 }
 
Index: linux-2.6.17/include/linux/pm.h
===================================================================
--- linux-2.6.17.orig/include/linux/pm.h
+++ linux-2.6.17/include/linux/pm.h
@@ -108,7 +108,59 @@ typedef int __bitwise suspend_state_t;
 #define PM_SUSPEND_STANDBY	((__force suspend_state_t) 1)
 #define PM_SUSPEND_MEM		((__force suspend_state_t) 3)
 #define PM_SUSPEND_DISK		((__force suspend_state_t) 4)
-#define PM_SUSPEND_MAX		((__force suspend_state_t) 5)
+#define PM_FREQ_CHANGE		((__force suspend_state_t) 5)
+#define PM_VOLT_CHANGE		((__force suspend_state_t) 6)
+#define PM_SUSPEND_MAX		((__force suspend_state_t) 7)
+
+#define PM_NAME_SIZE            16
+#define PM_DESCRIPTION_SIZE     48
+
+struct powerop {
+	struct list_head list;
+	suspend_state_t type;
+	char name[PM_NAME_SIZE];
+	char description[PM_DESCRIPTION_SIZE];
+	unsigned int frequency;		/* in KHz */
+	unsigned int voltage;		/* mV */
+	unsigned int latency;		/* transition latency in us */
+	int     (*prepare_transition)(struct powerop *cur, struct powerop *new);
+	int     (*transition)(struct powerop *cur, struct powerop *new);
+	int     (*finish_transition)(struct powerop *cur, struct powerop *new);
+
+	void *md_data;			/* arch dependent data (dpm_opt) */
+};
+
+struct constraint_param {
+	int id;
+	int min;
+	int max;
+};
+
+#define CONSTRAINT_PARAMS_MAX 20
+
+struct constraints {
+	int asserted;
+	int count;
+	int violations;
+	struct constraint_param param[CONSTRAINT_PARAMS_MAX];
+	struct list_head entry;
+};
+
+enum {
+	SCALE_PRECHANGE,
+	SCALE_POSTCHANGE,
+	SCALE_MAX
+};
+
+extern struct powerop pm_states;
+extern struct powerop *current_state;
+extern unsigned long powerop_compute_lpj(unsigned long ref, u_int div, u_int mult);
+struct notifier_block;
+extern void powerop_register_scale(struct notifier_block *nb, int level);
+extern void powerop_unregister_scale(struct notifier_block *nb, int level);
+extern int powerop_driver_scale(int level, struct powerop *new);
+extern void assert_constraints(struct constraints *);
+extern void deassert_constraints(struct constraints *);
 
 typedef int __bitwise suspend_disk_method_t;
 
@@ -128,7 +180,7 @@ struct pm_ops {
 
 extern void pm_set_ops(struct pm_ops *);
 extern struct pm_ops *pm_ops;
-extern int pm_suspend(suspend_state_t state);
+extern int pm_suspend(struct powerop *state);
 
 
 /*
Index: linux-2.6.17/kernel/power/power.h
===================================================================
--- linux-2.6.17.orig/kernel/power/power.h
+++ linux-2.6.17/kernel/power/power.h
@@ -113,4 +113,4 @@ extern int swsusp_resume(void);
 extern int swsusp_read(void);
 extern int swsusp_write(void);
 extern void swsusp_close(void);
-extern int suspend_enter(suspend_state_t state);
+extern int suspend_enter(struct powerop * state);
Index: linux-2.6.17/Documentation/power/powerop.txt
===================================================================
--- /dev/null
+++ linux-2.6.17/Documentation/power/powerop.txt
@@ -0,0 +1,212 @@
+
+The PowerOp Power Management infrastructure.
+
+David Singleton <dsingleton@mvista.com>
+
+25 July 2006
+
+Copyright (c) 2006 MontaVista Software Inc.
+
+0. Introduction
+
+The goal of PowerOp power management is to provide a framework that unifies
+and simplifies the various power management infrastructures in Linux.  The
+three infrastructures Power Op is concerned with are:
+
+	1) basic suspend/resume power management (CONFIG_PM)
+
+	2) basic processor frequency management (CONFIG_CPUFREQ)
+
+	3) SourceForge's Dynamic Power Management (CONFIG_DPM)
+
+All three power management infrastructures are concerned with controlling
+power states of the system, and interestingly enough they all perform the
+same basic operational steps to control changes in power state.
+
+PowerOp encapsulates supported system states into the concept of an
+operating point.  The operating point is defined by a simple structure
+that presents an architecture independent interface to the core power
+management framework and provides architecture/board dependent interface
+through both an op vector of function pointers to platform and operating
+point dependent functions.  The powerop structure also contains a
+pointer to the architecture and platform specific data needs to transition
+to the operating point.
+
+Encapsulated operating points can be manipulated via their name, making
+the user interface simple.  The interface to PowerOp is through two sysfs files.
+The first file, /sys/power/state, shows the operating point at which the
+system is currently operating.  The second file, /sys/power/supported_states,
+show the operating points supported by the system.  This file is a readonly
+file.  It cannot be changed by the user.  To transition to a new operating
+point the user, or powerop manager daemon, simply writes the name of
+the new operating point into /sys/power/state.
+
+PowerOp uses the existing power management sysfs infrastructure and extends it
+to perform cpufreq and dynamic power management operations. The traditional
+suspend to memory or disk (or swap) infrastructure has the correct operational
+structure that supports all types of power state change.
+
+1) Concepts borrowed from existing power management infrastructures.
+
+	a) The CPUFREQ table based frequency control makes controlling cpu
+	frequency simple and straight forward.  The user doesn't get to set
+	the cpu to any speed, but only to supported speeds that have been
+	provided by the hardware vendor and validated.
+
+	b) The steps to transition to an new operating point is correctly
+	defined by the three steps followed by suspend code:
+
+		step 1) suspend_prepare
+
+		step 2) suspend_enter
+
+		step 3) suspend_finish
+
+	These steps describe the steps used by both CPUFREQ and Dynamic Power
+	Management.
+
+	c) Dynamic Power Management treats all types of power states as
+	operating points, wether it's a suspend operating point, a particular
+	frequency, or a specific voltage.
+
+2) Unification of power management infrastructures in PowerOp.
+
+By combining the best of all of these power management infrastructures
+PowerOp uses the operational structure of tradition power management (PM) and
+converts all power states, frequency, voltage, idle or suspend to the CPUFREQ
+concept of only supported and validated operating points.
+
+PowerOp then becomes a simplified power management infrastructure in that
+only operating points that are supported and validated are available
+to the user.  Control of all operating points are done by the operating
+point name.  The user cannot supply invalid, or malicious,
+parameters that would hang or crash the system.
+
+1) PowerOp interface.
+
+To simplify power management all operations take place through two sysfs
+files, /sys/power/state and /sys/power/supported_states.  The 'state' file
+shows the current operating point of the system.  The readonly
+'supported_states' file shows the operating points the system supports.
+
+Supported operating points are displayed by name.
+
+The supported_states file contains rows of names.  The name space of operating
+points has been chosen to make the user space power manager simpler as well.
+The names PowerOp uses to define a basic or stock set of frequency based
+operating points are:
+
+		lowest
+		low
+		mediumlow
+		medium
+		mediumhigh
+		high
+		highest
+
+By using the same names regardless of the frequency behind the name
+the powerop manager can use some simple rules based decision making
+as to when to transition to a new operating point.  The power manager
+can then be constructed to make decisions based on speed versus power
+consumption without knowing exactly the frequencies or voltages behind
+the operating point names.  The names present operating points relationship
+to each other.
+
+To allow user space notification of events, like low battery, lid of
+the notebook being closed, etc.  PowerOp notifies the user through
+the hotplug interface.
+
+
+2) PowerOP Operating Points.
+
+An operating point is represented by the powerop struct which contains:
+
+struct powerop {
+        struct list_head list;
+        suspend_state_t type;
+        char name[PM_NAME_SIZE];
+        char description[PM_DESCRIPTION_SIZE];
+        unsigned int frequency;         /* in KHz */
+        unsigned int voltage;           /* mV */
+        unsigned int latency;           /* transition latency in us */
+        int     (*prepare_transition)(struct powerop *cur, struct powerop *new);
+        int     (*transition)(struct powerop *cur, struct powerop *new);
+        int     (*finish_transition)(struct powerop *cur, struct powerop *new);
+
+        void *md_data;                  /* arch dependent data  */
+};
+
+Each operating point has its own functions for preparing to transition,
+transitioning and finishing transition.  Cpu frequency operating points
+will probably share their op vectors, idle and suspend operating points my have
+different op vectors.
+
+
+3) Traditional Operation of Power Management Code.
+
+All three power management infrastructures have the same operational model.
+All three follow the PM model of preparing to suspend,  suspending,
+and finish the state change.  It was easiest to follow the model
+enforced by the traditional power management and use the three step process of:
+
+ 	1) get ready to change state
+	2) change state
+	3) finish changes
+
+Cpufreq infrastructure makes three calls to change the frequency of the
+processor:
+
+	1) cpufreq_notify_transition(&freq, CPUFREQ_PRECHANGE);
+
+	2) acpi_processor_set_performance (data, j, next_state);
+
+	3) cpufreq_notify_transition(&freq, CPUFREQ_POSTCHANGE);
+
+DPM uses these three calls to change frequency and/or voltage:
+
+	1) dpm_driver_scale(SCALE_PRECHANGE, new);
+
+	2) clk_set_rate(prcm_set, new->md_opt.prcm_clock);
+
+	3) dpm_driver_scale(SCALE_POSTCHANGE, new);
+
+PM uses these three calls to suspend:
+
+	1) suspend_prepare(state);
+
+	2) suspend_enter(state->type);
+
+	3) suspend_finish(state);
+
+
+4) PowerOP Operation.
+
+PowerOP uses the following three calls to transition to a new operating
+point.
+
+	prepare_to_transition(cur_state, new_state);
+
+	transition(cur_state, new_state);
+
+	finish_transistion(cur_state, new_state);
+
+The parameters are pointers to operating point structures, struct powerop.
+
+Power OP is a simplified version of all three of these infrastructures in
+that it only deals with operating points, and more specifically with
+supported operating points.  Power Op presents a set of supported operating
+points to the user.  This is similar to the cpufreq table concept in that
+only supported and validated frequencies are avaliable.
+
+The definition of the operating point is done in a manner similar to cpufreqs
+in that the supported operating frequency, voltage and transition latency,
+are predefined (by the hardware vendor) and validated.
+
+The user maninuplates the operting points of the system by the
+name of the operating points.  This simplifies both the code and the
+control of the system's operating points in the PowerOp daemon.
+
+All supported operating points are defined at compile time and
+the user sets the system to different operating points by
+the operating point name.
+
Index: linux-2.6.17/drivers/base/core.c
===================================================================
--- linux-2.6.17.orig/drivers/base/core.c
+++ linux-2.6.17/drivers/base/core.c
@@ -15,6 +15,7 @@
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/kdev_t.h>
+#include <linux/pm.h>
 
 #include <asm/semaphore.h>
 
@@ -362,6 +363,8 @@ int device_add(struct device *dev)
 		up(&dev->class->sem);
 	}
 
+	assert_constraints(dev->constraints);
+
 	/* notify platform of device entry */
 	if (platform_notify)
 		platform_notify(dev);
@@ -467,6 +470,8 @@ void device_del(struct device * dev)
 	}
 	device_remove_file(dev, &dev->uevent_attr);
 
+	deassert_constraints(dev->constraints);
+
 	/* Notify the platform of the removal, in case they
 	 * need to do anything...
 	 */
Index: linux-2.6.17/drivers/base/driver.c
===================================================================
--- linux-2.6.17.orig/drivers/base/driver.c
+++ linux-2.6.17/drivers/base/driver.c
@@ -12,6 +12,7 @@
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/string.h>
+#include <linux/pm.h>
 #include "base.h"
 
 #define to_dev(node) container_of(node, struct device, driver_list)
Index: linux-2.6.17/drivers/base/power/resume.c
===================================================================
--- linux-2.6.17.orig/drivers/base/power/resume.c
+++ linux-2.6.17/drivers/base/power/resume.c
@@ -10,6 +10,7 @@
 
 #include <linux/device.h>
 #include <linux/resume-trace.h>
+#include <linux/pm.h>
 #include "../base.h"
 #include "power.h"
 
@@ -37,6 +38,8 @@ int resume_device(struct device * dev)
 	if (dev->bus && dev->bus->resume) {
 		dev_dbg(dev,"resuming\n");
 		error = dev->bus->resume(dev);
+		if (!error)
+			assert_constraints(dev->constraints);
 	}
 	up(&dev->sem);
 	TRACE_RESUME(error);
Index: linux-2.6.17/drivers/base/power/suspend.c
===================================================================
--- linux-2.6.17.orig/drivers/base/power/suspend.c
+++ linux-2.6.17/drivers/base/power/suspend.c
@@ -75,6 +75,8 @@ int suspend_device(struct device * dev, 
 			);
 		error = dev->bus->suspend(dev, state);
 		suspend_report_result(dev->bus->suspend, error);
+		if (!error)
+			deassert_constraints(dev->constraints);
 	}
 	up(&dev->sem);
 	return error;
Index: linux-2.6.17/include/linux/device.h
===================================================================
--- linux-2.6.17.orig/include/linux/device.h
+++ linux-2.6.17/include/linux/device.h
@@ -333,6 +333,7 @@ struct device {
 
 	struct dma_coherent_mem	*dma_mem; /* internal for coherent mem
 					     override */
+	struct constraints      *constraints;
 
 	/* class_device migration path */
 	struct list_head	node;
Index: linux-2.6.17/drivers/base/power/powerop.c
===================================================================
--- /dev/null
+++ linux-2.6.17/drivers/base/power/powerop.c
@@ -0,0 +1,103 @@
+/*
+ * powerop.c -- PowerOp Power Management support (hotplug events and device
+ * scaling).
+ *
+ * (c) 2006 MontaVista Software, Inc. This file is licensed under the
+ * terms of the GNU General Public License version 2. This program is
+ * licensed "as is" without any warranty of any kind, whether express or
+ * implied.
+ */
+
+#include <linux/device.h>
+#include <linux/pm.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/notifier.h>
+
+#include "power.h"
+static RAW_NOTIFIER_HEAD(powerop_scale_notifier);
+static DECLARE_MUTEX(powerop_scale_sem);
+
+/* This function may be called by the platform frequency scaler before
+   or after a frequency change, in order to let drivers adjust any
+   clocks or calculations for the new frequency. */
+
+int powerop_driver_scale(int level, struct powerop *newop)
+{
+        if (down_trylock(&powerop_scale_sem))
+                return 1;
+
+        raw_notifier_call_chain(&powerop_scale_notifier, level, newop);
+        up(&powerop_scale_sem);
+	return 0;
+}
+
+void powerop_register_scale(struct notifier_block *nb, int level)
+{
+        down(&powerop_scale_sem);
+        raw_notifier_chain_register(&powerop_scale_notifier, nb);
+        up(&powerop_scale_sem);
+}
+
+void powerop_unregister_scale(struct notifier_block *nb, int level)
+{
+        down(&powerop_scale_sem);
+        raw_notifier_chain_unregister(&powerop_scale_notifier, nb);
+        up(&powerop_scale_sem);
+}
+
+EXPORT_SYMBOL(powerop_driver_scale);
+EXPORT_SYMBOL(powerop_register_scale);
+EXPORT_SYMBOL(powerop_unregister_scale);
+
+LIST_HEAD(powerop_constraints);
+DECLARE_MUTEX(powerop_constraints_sem);
+
+void assert_constraints(struct constraints *constraints)
+{
+        if (! constraints || constraints->asserted)
+                return;
+
+        down(&powerop_constraints_sem);
+        constraints->asserted = 1;
+        list_add_tail(&constraints->entry, &powerop_constraints);
+        up(&powerop_constraints_sem);
+
+}
+
+void deassert_constraints(struct constraints *constraints)
+{
+        if (! constraints || ! constraints->asserted)
+                return;
+
+        down(&powerop_constraints_sem);
+        constraints->asserted = 0;
+        list_del_init(&constraints->entry);
+        up(&powerop_constraints_sem);
+}
+
+EXPORT_SYMBOL(assert_constraints);
+EXPORT_SYMBOL(deassert_constraints);
+
+unsigned long powerop_compute_lpj(unsigned long ref, u_int div, u_int mult)
+{
+	unsigned long new_jiffy_l, new_jiffy_h;
+
+	/*
+	 * Recalculate loops_per_jiffy.  We do it this way to
+	 * avoid math overflow on 32-bit machines.  Maybe we
+	 * should make this architecture dependent?  If you have
+	 * a better way of doing this, please replace!
+	 *
+	 *    new = old * mult / div
+	 */
+	 new_jiffy_h = ref / div;
+	 new_jiffy_l = (ref % div) / 100;
+	 new_jiffy_h *= mult;
+	 new_jiffy_l = new_jiffy_l * mult / div;
+
+	 return new_jiffy_h + new_jiffy_l * 100;
+}
+
Index: linux-2.6.17/drivers/base/power/Makefile
===================================================================
--- linux-2.6.17.orig/drivers/base/power/Makefile
+++ linux-2.6.17/drivers/base/power/Makefile
@@ -1,4 +1,4 @@
-obj-y			:= shutdown.o
+obj-y			:= shutdown.o powerop.o
 obj-$(CONFIG_PM)	+= main.o suspend.o resume.o runtime.o sysfs.o
 obj-$(CONFIG_PM_TRACE)	+= trace.o
 

[-- Attachment #3: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Dynanic On-The-Fly Operating points for PowerOP
  2006-08-12  8:07       ` Vitaly Wool
                           ` (2 preceding siblings ...)
  2006-08-12 21:39         ` david singleton
@ 2006-08-12 21:40         ` david singleton
  2006-08-12 21:41         ` david singleton
  2006-08-12 23:14         ` Matthew Locke
  5 siblings, 0 replies; 15+ messages in thread
From: david singleton @ 2006-08-12 21:40 UTC (permalink / raw)
  To: Vitaly Wool; +Cc: linux-pm

[-- Attachment #1: Type: text/plain, Size: 104 bytes --]


Here's the powerop-cpufreq.patch.  It hasn't changed from the 
2.6.18-rc1 version (I think).


David



[-- Attachment #2: powerop-cpufreq.patch --]
[-- Type: application/octet-stream, Size: 3225 bytes --]


Signed-Off-by: David Singleton <dsingleton@mvista.com>

 drivers/cpufreq/cpufreq.c |   40 ++++++++++++++++++++++++++++++++++++++--
 include/linux/cpufreq.h   |    2 ++
 2 files changed, 40 insertions(+), 2 deletions(-)

Index: linux-2.6.17/drivers/cpufreq/cpufreq.c
===================================================================
--- linux-2.6.17.orig/drivers/cpufreq/cpufreq.c
+++ linux-2.6.17/drivers/cpufreq/cpufreq.c
@@ -213,19 +213,48 @@ static void adjust_jiffies(unsigned long
 	if (!l_p_j_ref_freq) {
 		l_p_j_ref = loops_per_jiffy;
 		l_p_j_ref_freq = ci->old;
-		dprintk("saving %lu as reference value for loops_per_jiffy; freq is %u kHz\n", l_p_j_ref, l_p_j_ref_freq);
+		printk("saving %lu as reference value for loops_per_jiffy; freq is %u kHz\n", l_p_j_ref, l_p_j_ref_freq);
 	}
 	if ((val == CPUFREQ_PRECHANGE  && ci->old < ci->new) ||
 	    (val == CPUFREQ_POSTCHANGE && ci->old > ci->new) ||
 	    (val == CPUFREQ_RESUMECHANGE || val == CPUFREQ_SUSPENDCHANGE)) {
 		loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq, ci->new);
-		dprintk("scaling loops_per_jiffy to %lu for frequency %u kHz\n", loops_per_jiffy, ci->new);
+		printk("scaling loops_per_jiffy to %lu for frequency %u kHz\n", loops_per_jiffy, ci->new);
 	}
 }
 #else
 static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) { return; }
 #endif
 
+int cpufreq_prepare_transition(struct powerop *cur, struct powerop *new)
+{
+	struct cpufreq_freqs freqs;
+
+	freqs.old = cur->frequency;
+	freqs.new = new->frequency;
+	freqs.cpu = 0;
+	freqs.flags = cpufreq_driver->flags;
+	blocking_notifier_call_chain(&cpufreq_transition_notifier_list,
+			CPUFREQ_PRECHANGE, &freqs);
+	adjust_jiffies(CPUFREQ_PRECHANGE, &freqs);
+	return 0;
+}
+EXPORT_SYMBOL(cpufreq_prepare_transition);
+
+int cpufreq_finish_transition(struct powerop *cur, struct powerop *new)
+{
+	struct cpufreq_freqs freqs;
+
+	freqs.old = cur->frequency;
+	freqs.new = new->frequency;
+	freqs.cpu = 0;
+	freqs.flags = cpufreq_driver->flags;
+	adjust_jiffies(CPUFREQ_POSTCHANGE, &freqs);
+	blocking_notifier_call_chain(&cpufreq_transition_notifier_list,
+			CPUFREQ_POSTCHANGE, &freqs);
+	return 0;
+}
+EXPORT_SYMBOL(cpufreq_finish_transition);
 
 /**
  * cpufreq_notify_transition - call notifier chain and adjust_jiffies
@@ -920,6 +949,12 @@ static void cpufreq_out_of_sync(unsigned
 }
 
 
+#ifdef CONFIG_PM
+unsigned int cpufreq_quick_get(unsigned int cpu)
+{
+	return (current_state->frequency * 1000);
+}
+#else
 /**
  * cpufreq_quick_get - get the CPU frequency (in kHz) frpm policy->cur
  * @cpu: CPU number
@@ -941,6 +976,7 @@ unsigned int cpufreq_quick_get(unsigned 
 
 	return (ret);
 }
+#endif
 EXPORT_SYMBOL(cpufreq_quick_get);
 
 
Index: linux-2.6.17/include/linux/cpufreq.h
===================================================================
--- linux-2.6.17.orig/include/linux/cpufreq.h
+++ linux-2.6.17/include/linux/cpufreq.h
@@ -268,6 +268,8 @@ static inline unsigned int cpufreq_quick
 	return 0;
 }
 #endif
+int cpufreq_prepare_transition(struct powerop *cur, struct powerop *new);
+int cpufreq_finish_transition(struct powerop *cur, struct powerop *new);
 
 
 /*********************************************************************

[-- Attachment #3: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Dynanic On-The-Fly Operating points for PowerOP
  2006-08-12  8:07       ` Vitaly Wool
                           ` (3 preceding siblings ...)
  2006-08-12 21:40         ` david singleton
@ 2006-08-12 21:41         ` david singleton
  2006-08-16 15:02           ` Len Brown
  2006-08-12 23:14         ` Matthew Locke
  5 siblings, 1 reply; 15+ messages in thread
From: david singleton @ 2006-08-12 21:41 UTC (permalink / raw)
  To: Vitaly Wool; +Cc: linux-pm

[-- Attachment #1: Type: text/plain, Size: 160 bytes --]

Here's the powerop-x86-centrino.patch.   It's only change from the 
2.6.18-rc1 version
is to change names  of operating points from med to medium, etc.

David


[-- Attachment #2: powerop-x86-centrino.patch --]
[-- Type: application/octet-stream, Size: 17191 bytes --]


Signed-Off-by: David Singleton <dsingleton@mvista.com>

 arch/i386/kernel/cpu/Makefile                           |    1 
 arch/i386/kernel/cpu/powerop/Makefile                   |    2 
 arch/i386/kernel/cpu/powerop/centrino-dynamic-powerop.c |   71 ++
 arch/i386/kernel/cpu/powerop/centrino-powerop.c         |  465 ++++++++++++++++
 arch/i386/kernel/i386_ksyms.c                           |    4 
 5 files changed, 543 insertions(+)

Index: linux-2.6.17/arch/i386/kernel/cpu/Makefile
===================================================================
--- linux-2.6.17.orig/arch/i386/kernel/cpu/Makefile
+++ linux-2.6.17/arch/i386/kernel/cpu/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_X86_MCE)	+=	mcheck/
 
 obj-$(CONFIG_MTRR)	+= 	mtrr/
 obj-$(CONFIG_CPU_FREQ)	+=	cpufreq/
+obj-$(CONFIG_PM)	+=	powerop/
Index: linux-2.6.17/arch/i386/kernel/cpu/powerop/Makefile
===================================================================
--- /dev/null
+++ linux-2.6.17/arch/i386/kernel/cpu/powerop/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_X86_SPEEDSTEP_CENTRINO)	+= centrino-powerop.o
+obj-m					+= centrino-dynamic-powerop.o
Index: linux-2.6.17/arch/i386/kernel/cpu/powerop/centrino-dynamic-powerop.c
===================================================================
--- /dev/null
+++ linux-2.6.17/arch/i386/kernel/cpu/powerop/centrino-dynamic-powerop.c
@@ -0,0 +1,71 @@
+/*
+ * powerop/centrino-dynamic-powerop.c
+ *
+ * This is the template to create dynamic operating points for power management.
+ *
+ * Author: David Singleton dsingleton@mvista.com MontaVista Software, Inc.
+ *
+ * 2006 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/pm.h>
+#include <linux/cpufreq.h>
+#include <linux/moduleparam.h>
+#include <linux/moduleloader.h>
+
+int centrino_transition(struct powerop *cur, struct powerop *new);
+
+static char powerop_name[PM_NAME_SIZE] = "dynamic";
+static unsigned int voltage = 1308;
+static unsigned int latency = 100;
+module_param_named(name, powerop_name, char *, 0);
+module_param_named(frequency, frequency, uint, 0);
+module_param_named(voltage, voltage, uint, 0);
+module_param_named(latency, latency, uint, 0);
+MODULE_PARM_DESC(frequency, "cpu frequency in kHz");
+MODULE_PARM_DESC(voltage, "cpu voltage in mV");
+MODULE_PARM_DESC(latency, "transition latency in us");
+
+/* Register both the driver and the device */
+
+static struct powerop dynamic_op = {
+	.type = PM_FREQ_CHANGE,
+	.name = "Dynamic",
+	.description = "Dynamic PowerOp point for Speedstep Centrino",
+	.prepare_transition = cpufreq_prepare_transition,
+	.transition = centrino_transition,
+	.finish_transition = cpufreq_finish_transition,
+};
+
+extern void centrino_set_frequency(struct powerop *op, uint freq, uint volt);
+
+int __init dynamic_powerop_init(void)
+{
+
+	printk("Dynamic PowerOp operating point for speedstep centrino\n");
+	dynamic_op.frequency = frequency;
+	dynamic_op.voltage = voltage;
+	dynamic_op.latency = latency;
+	centrino_set_frequency(&dynamic_op, frequency / 1000, voltage);
+	printk("freq %d volt %d msr 0x%x\n", dynamic_op.frequency,
+	   dynamic_op.voltage, (unsigned int)dynamic_op.md_data);
+	list_add_tail(&dynamic_op.list, &pm_states.list);
+	return 0;
+}
+
+void __exit dynamic_powerop_cleanup(void)
+{
+	list_del_init(&dynamic_op.list);
+}
+
+module_init(dynamic_powerop_init);
+module_exit(dynamic_powerop_cleanup);
+
+MODULE_DESCRIPTION("Dynamic Powerop module");
+MODULE_LICENSE("GPL");
Index: linux-2.6.17/arch/i386/kernel/cpu/powerop/centrino-powerop.c
===================================================================
--- /dev/null
+++ linux-2.6.17/arch/i386/kernel/cpu/powerop/centrino-powerop.c
@@ -0,0 +1,465 @@
+/*
+ * PowerOp support for Enhanced SpeedStep, as found in Intel's Pentium
+ * M (part of the Centrino chipset).
+ *
+ * Modelled on speedstep-centrino.c
+ *
+ * Copyright (C) 2006 David Singleton <dsingleton@mvista.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/compiler.h>
+
+#include <asm/msr.h>
+#include <asm/processor.h>
+#include <asm/cpufeature.h>
+
+struct cpu_id
+{
+	__u8	x86;            /* CPU family */
+	__u8	x86_model;	/* model */
+	__u8	x86_mask;	/* stepping */
+};
+
+enum {
+	CPU_BANIAS,
+	CPU_DOTHAN_A1,
+	CPU_DOTHAN_A2,
+	CPU_DOTHAN_B0,
+	CPU_MP4HT_D0,
+	CPU_MP4HT_E0,
+};
+
+static const struct cpu_id cpu_ids[] = {
+	[CPU_BANIAS]	= { 6,  9, 5 },
+	[CPU_DOTHAN_A1]	= { 6, 13, 1 },
+	[CPU_DOTHAN_A2]	= { 6, 13, 2 },
+	[CPU_DOTHAN_B0]	= { 6, 13, 6 },
+	[CPU_MP4HT_D0]	= {15,  3, 4 },
+	[CPU_MP4HT_E0]	= {15,  4, 1 },
+};
+#define N_IDS	ARRAY_SIZE(cpu_ids)
+
+struct cpu_model
+{
+	const struct cpu_id *cpu_id;
+	const char	*model_name;
+	unsigned	max_freq; /* max clock in kHz */
+
+	struct cpufreq_frequency_table *op_points; /* clock/voltage pairs */
+};
+static int centrino_verify_cpu_id(const struct cpuinfo_x86 *c, const struct cpu_id *x);
+
+void centrino_set_frequency(struct powerop *op, uint freq, uint volt)
+{
+	op->frequency = freq * 1000;
+	op->voltage = volt;
+	op->md_data = (void *)(((freq / 100) << 8) | (volt - 700) / 16);
+	printk("freq %d volt %d msr 0x%x\n", op->frequency, op->voltage,
+	    (unsigned int)op->md_data);
+}
+EXPORT_SYMBOL(centrino_set_frequency);
+
+int centrino_transition(struct powerop *cur, struct powerop *new)
+{
+	unsigned int msr, oldmsr = 0, h = 0;
+
+	if (cur == new)
+		return 0;
+
+	msr = (unsigned int)new->md_data;
+	rdmsr(MSR_IA32_PERF_CTL, oldmsr, h);
+
+	/* all but 16 LSB are reserved, treat them with care */
+	oldmsr &= ~0xffff;
+	msr &= 0xffff;
+	oldmsr |= msr;
+
+	wrmsr(MSR_IA32_PERF_CTL, oldmsr, h);
+
+	return 0;
+}
+EXPORT_SYMBOL(centrino_transition);
+
+#define OP(mhz, mv)                                                     \
+        {                                                               \
+                .frequency = (mhz) * 1000,                              \
+                .index = (((mhz)/100) << 8) | ((mv - 700) / 16)         \
+        }
+
+/*
+ * These voltage tables were derived from the Intel Pentium M
+ * datasheet, document 25261202.pdf, Table 5.  I have verified they
+ * are consistent with my IBM ThinkPad X31, which has a 1.3GHz Pentium
+ * M.
+ */
+
+/* Ultra Low Voltage Intel Pentium M processor 900MHz (Banias) */
+static struct cpufreq_frequency_table banias_900[] =
+{
+        OP(600,  844),
+        OP(800,  988),
+        OP(900, 1004),
+        { .frequency = CPUFREQ_TABLE_END }
+};
+/* Ultra Low Voltage Intel Pentium M processor 1000MHz (Banias) */
+static struct cpufreq_frequency_table banias_1000[] =
+{
+        OP(600,   844),
+        OP(800,   972),
+        OP(900,   988),
+        OP(1000, 1004),
+        { .frequency = CPUFREQ_TABLE_END }
+};
+
+/* Low Voltage Intel Pentium M processor 1.10GHz (Banias) */
+static struct cpufreq_frequency_table banias_1100[] =
+{
+        OP( 600,  956),
+        OP( 800, 1020),
+        OP( 900, 1100),
+        OP(1000, 1164),
+        OP(1100, 1180),
+        { .frequency = CPUFREQ_TABLE_END }
+};
+
+
+/* Low Voltage Intel Pentium M processor 1.20GHz (Banias) */
+static struct cpufreq_frequency_table banias_1200[] =
+{
+        OP( 600,  956),
+        OP( 800, 1004),
+        OP( 900, 1020),
+        OP(1000, 1100),
+        OP(1100, 1164),
+        OP(1200, 1180),
+        { .frequency = CPUFREQ_TABLE_END }
+};
+
+/* Intel Pentium M processor 1.30GHz (Banias) */
+static struct cpufreq_frequency_table banias_1300[] =
+{
+        OP( 600,  956),
+        OP( 800, 1260),
+        OP(1000, 1292),
+        OP(1200, 1356),
+        OP(1300, 1388),
+        { .frequency = CPUFREQ_TABLE_END }
+};
+
+/* Intel Pentium M processor 1.40GHz (Banias) */
+static struct cpufreq_frequency_table banias_1400[] =
+{
+        OP( 600,  956),
+        OP( 800, 1180),
+        OP(1000, 1308),
+        OP(1200, 1436),
+        OP(1400, 1484),
+        { .frequency = CPUFREQ_TABLE_END }
+};
+
+/* Intel Pentium M processor 1.50GHz (Banias) */
+static struct cpufreq_frequency_table banias_1500[] =
+{
+        OP( 600,  956),
+        OP( 800, 1116),
+        OP(1000, 1228),
+        OP(1200, 1356),
+        OP(1400, 1452),
+        OP(1500, 1484),
+        { .frequency = CPUFREQ_TABLE_END }
+};
+
+/* Intel Pentium M processor 1.60GHz (Banias) */
+static struct cpufreq_frequency_table banias_1600[] =
+{
+        OP( 600,  956),
+        OP( 800, 1036),
+        OP(1000, 1164),
+        OP(1200, 1276),
+        OP(1400, 1420),
+        OP(1600, 1484),
+        { .frequency = CPUFREQ_TABLE_END }
+};
+
+/* Intel Pentium M processor 1.70GHz (Banias) */
+static struct cpufreq_frequency_table banias_1700[] =
+{
+        OP( 600,  956),
+        OP( 800, 1004),
+        OP(1000, 1116),
+        OP(1200, 1228),
+        OP(1400, 1308),
+        OP(1700, 1484),
+        { .frequency = CPUFREQ_TABLE_END }
+};
+
+#define _BANIAS(cpuid, max, name)	\
+{	.cpu_id		= cpuid,	\
+	.model_name	= "Intel(R) Pentium(R) M processor " name "MHz", \
+	.max_freq	= (max)*1000,	\
+	.op_points	= banias_##max,	\
+}
+#define BANIAS(max)	_BANIAS(&cpu_ids[CPU_BANIAS], max, #max)
+
+static struct powerop lowest = {
+	.name = "lowest",
+	.description = "Lowest Frequency state",
+	.type = PM_FREQ_CHANGE,
+	.frequency = 0,
+	.voltage = 0,
+	.latency = 100,
+	.prepare_transition  = cpufreq_prepare_transition,
+	.transition = centrino_transition,
+	.finish_transition = cpufreq_finish_transition,
+};
+
+static struct powerop low = {
+	.name = "low",
+	.description = "Low Frequency state",
+	.type = PM_FREQ_CHANGE,
+	.latency = 100,
+	.prepare_transition  = cpufreq_prepare_transition,
+	.transition = centrino_transition,
+	.finish_transition = cpufreq_finish_transition,
+};
+
+static struct powerop mediumlow = {
+	.name = "mediumlow",
+	.description = "Medium Low Frequency state",
+	.type = PM_FREQ_CHANGE,
+	.latency = 100,
+	.prepare_transition  = cpufreq_prepare_transition,
+	.transition = centrino_transition,
+	.finish_transition = cpufreq_finish_transition,
+};
+
+static struct powerop medium = {
+	.name = "medium",
+	.description = "Medium Frequency state",
+	.type = PM_FREQ_CHANGE,
+	.latency = 100,
+	.prepare_transition  = cpufreq_prepare_transition,
+	.transition = centrino_transition,
+	.finish_transition = cpufreq_finish_transition,
+};
+
+static struct powerop mediumhigh = {
+	.name = "mediumhigh",
+	.description = "Medium High Frequency state",
+	.type = PM_FREQ_CHANGE,
+	.latency = 100,
+	.prepare_transition  = cpufreq_prepare_transition,
+	.transition = centrino_transition,
+	.finish_transition = cpufreq_finish_transition,
+};
+
+static struct powerop high = {
+	.name = "high",
+	.description = "High Frequency state",
+	.type = PM_FREQ_CHANGE,
+	.latency = 100,
+	.prepare_transition  = cpufreq_prepare_transition,
+	.transition = centrino_transition,
+	.finish_transition = cpufreq_finish_transition,
+};
+
+static struct powerop highest = {
+	.name = "highest",
+	.description = "Highest Frequency state",
+	.type = PM_FREQ_CHANGE,
+	.latency = 100,
+	.prepare_transition  = cpufreq_prepare_transition,
+	.transition = centrino_transition,
+	.finish_transition = cpufreq_finish_transition,
+};
+
+/* CPU models, their operating frequency range, and freq/voltage
+   operating points */
+static struct cpu_model models[] =
+{
+	_BANIAS(&cpu_ids[CPU_BANIAS], 900, " 900"),
+	BANIAS(1000),
+	BANIAS(1100),
+	BANIAS(1200),
+	BANIAS(1300),
+	BANIAS(1400),
+	BANIAS(1500),
+	BANIAS(1600),
+	BANIAS(1700),
+
+	/* NULL model_name is a wildcard */
+	{ &cpu_ids[CPU_DOTHAN_A1], NULL, 0, NULL },
+	{ &cpu_ids[CPU_DOTHAN_A2], NULL, 0, NULL },
+	{ &cpu_ids[CPU_DOTHAN_B0], NULL, 0, NULL },
+	{ &cpu_ids[CPU_MP4HT_D0], NULL, 0, NULL },
+	{ &cpu_ids[CPU_MP4HT_E0], NULL, 0, NULL },
+
+	{ NULL, }
+};
+#undef _BANIAS
+#undef BANIAS
+
+static int __init centrino_init_powerop(void)
+{
+	struct cpuinfo_x86 *cpu = &cpu_data[0];
+	struct cpu_model *model;
+
+	for(model = models; model->cpu_id != NULL; model++) {
+		if (centrino_verify_cpu_id(cpu, model->cpu_id) &&
+		    (model->model_name == NULL ||
+		     strcmp(cpu->x86_model_id, model->model_name) == 0))
+			break;
+	}
+
+	if (model->cpu_id == NULL) {
+		/* No match at all */
+		printk("no support for CPU model %s\n", cpu->x86_model_id);
+		return -ENOENT;
+	}
+
+	printk("found \"%s\": max frequency: %dkHz\n",
+	       model->model_name, model->max_freq);
+	switch (model->max_freq) {
+	    case (900000) :
+	    {
+		centrino_set_frequency(&low, 600, 844);
+		centrino_set_frequency(&medium, 800, 988);
+		centrino_set_frequency(&high, 900, 1004);
+		break;
+	    }
+	    case (1000000) :
+	    {
+		centrino_set_frequency(&low, 600, 844);
+		centrino_set_frequency(&medium, 800, 972);
+		centrino_set_frequency(&high, 900, 988);
+		centrino_set_frequency(&highest, 1000, 1004);
+		break;
+	    }
+	    case (1100000) :
+	    {
+		centrino_set_frequency(&lowest, 600, 956);
+		centrino_set_frequency(&low, 800, 1020);
+		centrino_set_frequency(&medium, 900, 1100);
+		centrino_set_frequency(&high, 1000, 1164);
+		centrino_set_frequency(&highest, 1100, 1180);
+		break;
+	    }
+	    case (1200000) :
+	    {
+		centrino_set_frequency(&lowest, 600, 956);
+		centrino_set_frequency(&low, 800, 1004);
+		centrino_set_frequency(&medium, 900, 1020);
+		centrino_set_frequency(&mediumhigh, 1000, 1100);
+		centrino_set_frequency(&high, 1100, 1164);
+		centrino_set_frequency(&highest, 1200, 1180);
+		break;
+	    }
+	    case (1300000) :
+	    {
+		centrino_set_frequency(&lowest, 600, 956);
+		centrino_set_frequency(&low, 800, 1260);
+		centrino_set_frequency(&medium, 1000, 1292);
+		centrino_set_frequency(&high, 1200, 1356);
+		centrino_set_frequency(&highest, 1300, 1388);
+		break;
+	    }
+	    case (1400000) :
+	    {
+		centrino_set_frequency(&lowest, 600, 956);
+		centrino_set_frequency(&low, 800, 1180);
+		centrino_set_frequency(&medium, 1000, 1308);
+		centrino_set_frequency(&high, 1200, 1436);
+		centrino_set_frequency(&highest, 1400, 1484);
+		break;
+	    }
+	    case (1500000) :
+	    {
+		centrino_set_frequency(&lowest, 600, 956);
+		centrino_set_frequency(&low, 800, 1116);
+		centrino_set_frequency(&medium, 1000, 1228);
+		centrino_set_frequency(&mediumhigh, 1200, 1356);
+		centrino_set_frequency(&high, 1400, 1452);
+		centrino_set_frequency(&highest, 1500, 1484);
+		break;
+	    }
+	    case (1600000) :
+	    {
+		centrino_set_frequency(&lowest, 600, 956);
+		centrino_set_frequency(&low, 800, 1036);
+		centrino_set_frequency(&medium, 1000, 1164);
+		centrino_set_frequency(&mediumhigh, 1200, 1276);
+		centrino_set_frequency(&high, 1400, 1420);
+		centrino_set_frequency(&highest, 1600, 1484);
+		break;
+	    }
+	    case (1700000) :
+	    {
+		centrino_set_frequency(&lowest, 600, 956);
+		centrino_set_frequency(&low, 800, 1004);
+		centrino_set_frequency(&medium, 1000, 1116);
+		centrino_set_frequency(&mediumhigh, 1200, 1228);
+		centrino_set_frequency(&high, 1400, 1308);
+		centrino_set_frequency(&highest, 1700, 1484);
+		break;
+	    }
+	}
+	if (lowest.frequency)
+		list_add_tail(&lowest.list, &pm_states.list);
+	if (low.frequency)
+		list_add_tail(&low.list, &pm_states.list);
+	if (mediumlow.frequency)
+		list_add_tail(&mediumlow.list, &pm_states.list);
+	if (medium.frequency)
+		list_add_tail(&medium.list, &pm_states.list);
+	if (mediumhigh.frequency)
+		list_add_tail(&mediumhigh.list, &pm_states.list);
+	if (high.frequency) {
+		list_add_tail(&high.list, &pm_states.list);
+		current_state = &high;
+	}
+	if (highest.frequency) {
+		list_add_tail(&highest.list, &pm_states.list);
+		current_state = &highest;
+	}
+	return 0;
+}
+
+static void centrino_exit_powerop(void)
+{
+	if (lowest.frequency)
+		list_del_init(&lowest.list);
+	if (low.frequency)
+		list_del_init(&low.list);
+	if (mediumlow.frequency)
+		list_del_init(&mediumlow.list);
+	if (medium.frequency)
+		list_del_init(&medium.list);
+	if (mediumhigh.frequency)
+		list_del_init(&mediumhigh.list);
+	if (high.frequency)
+		list_del_init(&high.list);
+	if (highest.frequency)
+		list_del_init(&highest.list);
+	return;
+}
+
+static int centrino_verify_cpu_id(const struct cpuinfo_x86 *c, const struct cpu_id *x)
+{
+	if ((c->x86 == x->x86) &&
+	    (c->x86_model == x->x86_model) &&
+	    (c->x86_mask == x->x86_mask))
+		return 1;
+	return 0;
+}
+
+MODULE_AUTHOR ("David Singleton <dsingleton@mvista.com>");
+MODULE_DESCRIPTION ("PowerOp operting points for Intel Pentium M processors.");
+MODULE_LICENSE ("GPL");
+
+late_initcall(centrino_init_powerop);
+module_exit(centrino_exit_powerop);
Index: linux-2.6.17/arch/i386/kernel/i386_ksyms.c
===================================================================
--- linux-2.6.17.orig/arch/i386/kernel/i386_ksyms.c
+++ linux-2.6.17/arch/i386/kernel/i386_ksyms.c
@@ -28,3 +28,7 @@ EXPORT_SYMBOL(__read_lock_failed);
 #endif
 
 EXPORT_SYMBOL(csum_partial);
+#ifdef CONFIG_PM
+#include <linux/pm.h>
+EXPORT_SYMBOL(pm_states);
+#endif

[-- Attachment #3: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Dynanic On-The-Fly Operating points for PowerOP
  2006-08-12  8:07       ` Vitaly Wool
                           ` (4 preceding siblings ...)
  2006-08-12 21:41         ` david singleton
@ 2006-08-12 23:14         ` Matthew Locke
  2006-08-13  2:25           ` Preece Scott-PREECE
  2006-08-14  3:37           ` david singleton
  5 siblings, 2 replies; 15+ messages in thread
From: Matthew Locke @ 2006-08-12 23:14 UTC (permalink / raw)
  To: Vitaly Wool; +Cc: linux-pm, david singleton


On Aug 12, 2006, at 1:07 AM, Vitaly Wool wrote:
> May I disagree? Having an alternative implementation is never a bad
> thing, unless the sides are unable to co-operate ;)
> Let's try to compare implementations and their concepts, and benefit 
> from both.

What are you disagreeing with?  Re-read my statement below.   I don't 
see the reason for another implementation.  Rather than guess,  I would 
prefer that Dave tell us why he is submitting a different powerop 
interface.  There must be something driving him to do so.

>> Is there
>> something specific missing or wrong with the patches we submitted that
>> required another set of patches to be developed?  By joining in the
>> discussion, I mean that you should let us know this information.  If
>> patches are your method for doing so, then at least provide a
>> description of what your patches address that ours does not.  Right
>> now, its just unclear why there are two different powerop patchsets.
>>

Matt

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Dynanic On-The-Fly Operating points for PowerOP
  2006-08-12 23:14         ` Matthew Locke
@ 2006-08-13  2:25           ` Preece Scott-PREECE
  2006-08-14  3:37           ` david singleton
  1 sibling, 0 replies; 15+ messages in thread
From: Preece Scott-PREECE @ 2006-08-13  2:25 UTC (permalink / raw)
  To: Matthew Locke, Vitaly Wool; +Cc: linux-pm, david singleton

I'd say that David's recent note provides some useful information about
why his implementation is different. I don't mind seeing an alternate
implementation if there's a reason for it, but I have to say that it
would have been nice to have the exegesis with or before the patches.

scott

-----Original Message-----
From: linux-pm-bounces@lists.osdl.org
[mailto:linux-pm-bounces@lists.osdl.org] On Behalf Of Matthew Locke
Sent: Saturday, August 12, 2006 6:15 PM
To: Vitaly Wool
Cc: linux-pm@lists.osdl.org; david singleton
Subject: Re: [linux-pm] Dynanic On-The-Fly Operating points for PowerOP


On Aug 12, 2006, at 1:07 AM, Vitaly Wool wrote:
> May I disagree? Having an alternative implementation is never a bad 
> thing, unless the sides are unable to co-operate ;) Let's try to 
> compare implementations and their concepts, and benefit from both.

What are you disagreeing with?  Re-read my statement below.   I don't 
see the reason for another implementation.  Rather than guess,  I would
prefer that Dave tell us why he is submitting a different powerop
interface.  There must be something driving him to do so.

>> Is there
>> something specific missing or wrong with the patches we submitted 
>> that required another set of patches to be developed?  By joining in 
>> the discussion, I mean that you should let us know this information.

>> If patches are your method for doing so, then at least provide a 
>> description of what your patches address that ours does not.  Right 
>> now, its just unclear why there are two different powerop patchsets.
>>

Matt

_______________________________________________
linux-pm mailing list
linux-pm@lists.osdl.org
https://lists.osdl.org/mailman/listinfo/linux-pm

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Dynanic On-The-Fly Operating points for PowerOP
  2006-08-12 23:14         ` Matthew Locke
  2006-08-13  2:25           ` Preece Scott-PREECE
@ 2006-08-14  3:37           ` david singleton
  1 sibling, 0 replies; 15+ messages in thread
From: david singleton @ 2006-08-14  3:37 UTC (permalink / raw)
  To: Matthew Locke; +Cc: linux-pm


On Aug 12, 2006, at 4:14 PM, Matthew Locke wrote:

>
> On Aug 12, 2006, at 1:07 AM, Vitaly Wool wrote:
>> May I disagree? Having an alternative implementation is never a bad
>> thing, unless the sides are unable to co-operate ;)
>> Let's try to compare implementations and their concepts, and benefit 
>> from both.
>
> What are you disagreeing with?  Re-read my statement below.   I don't 
> see the reason for another implementation.  Rather than guess,  I 
> would prefer that Dave tell us why he is submitting a different 
> powerop interface.  There must be something driving him to do so.

Okay, let me see if I can clarify a bit.  I presented the alternative
implementation to compare and contrast ideas, and because I believe this
implementation is a better solution for the following reasons:

I believe this interface between the kernel and user space is cleaner.  
The
system presents the list of supported operating points to the user and 
the
user invokes them by their name.

/sys/power/state is used for setting and displaying the current 
operating point
and /sys/power/supported_states presents the names of the supported 
operating
points to the user.  That's the entire user interface.

I believe the moving of policies, classes and governors out of the 
kernel
and into user space is a better solution.  The user, or power manager 
daemon,
should make the decisions about when and what operating point to set, 
and
which devices to suspend or resume.

The user does not pass in information needed to construct an operating 
point
and let the kernel try and validate the data and protect against 
incorrect
or malicious data that would hang the system.  Operating points are 
constructed
from the hardware vendor supplied data sheets and validated, just like 
cpufreqs,
and tested before the code is integrated into the mainstream.

I believe the architecture independent interface between the kernel and
powerop framework is better and I believe the architecture 
dependent/board
specific interface  is better because:

The entire design revolves around operating points and operating points
can be encapsulated into two data structures and three functions.  The 
powerop
structure contains the architecture independent data and function 
pointers
to the platform dependent routines to transition to the operating point.
The md_data pointer in the powerop structure points to any kind of 
platform
specific data needed by the transition routines.

The platform dependent interface not only lets each platform have its 
own
information and functions for operating state transition, but it also 
lets
individual operating points on the same platform have operating point
specific data and functions.

By encapsulating an operating point into two data structures new 
operating
points can easily be constructed.

Lets consider for a moment the case where Intel creates a new revision 
of the
pxa27x processor for the mainstone.  To implement support for the new 
processor
one would only have to create a set of powerop structures and md_opt 
structures
that contain the information for the new processor's operating points.

The next step would be to insert the new processor identification into
the case statement where the system deteremines the processor id and the
new operating points would be linked into the system you'd be done
implementing a new set of operating points for a new processor.

This would take a matter of hours to create and test new operating 
points
instead of days or weeks.  And with the dynamic on-the-fly loadable 
module
for an operating points creation and testing of new processors would
take a very short time, something the SoC manufacturers might like.

And by doing the mainstone patch wthout having to change any of the 
framework
or interfaces I've convinced myself that the concept, framework and 
interfaces
will work across different architectures and platforms.

Its simple but enormously flexible.


David



>
>>> Is there
>>> something specific missing or wrong with the patches we submitted 
>>> that
>>> required another set of patches to be developed?  By joining in the
>>> discussion, I mean that you should let us know this information.  If
>>> patches are your method for doing so, then at least provide a
>>> description of what your patches address that ours does not.  Right
>>> now, its just unclear why there are two different powerop patchsets.
>>>
>
> Matt
>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Dynanic On-The-Fly Operating points for PowerOP
  2006-08-08 18:12 Dynanic On-The-Fly Operating points for PowerOP David Singleton
  2006-08-09 21:17 ` Matthew Locke
@ 2006-08-15 19:44 ` Pavel Machek
  1 sibling, 0 replies; 15+ messages in thread
From: Pavel Machek @ 2006-08-15 19:44 UTC (permalink / raw)
  To: David Singleton; +Cc: linux-pm

Hi!

>         2) The naming scheme for operating points has been unified to
>         provide a better interface to the PowerOp power manager daemon.
>         The names range from:
> 
>                         highest
>                         high
>                         medhigh
>                         medium
>                         medlow
>                         low
>                         lowest
> 
>         PowerOp maps the supported processor frequencies onto this
>         namespace list.  The set of centrino processors it supports have
>         supported sets of between four and six different operating points.
> 
>         The PowerOP daemon, coming soon, can simply read the supported
>         set of operating points and make some simple rules based
>         decisions about when to transition to various operating points.
> 
>         The goal of a unified name space is to provide a PowerOp manager
>         that runs out of the box, with very little setup by the user.

Well, we already have perfectly working API for frequency management,
why not use that?

-- 
Thanks for all the (sleeping) penguins.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Dynanic On-The-Fly Operating points for PowerOP
  2006-08-12 21:41         ` david singleton
@ 2006-08-16 15:02           ` Len Brown
  0 siblings, 0 replies; 15+ messages in thread
From: Len Brown @ 2006-08-16 15:02 UTC (permalink / raw)
  To: linux-pm; +Cc: david singleton

On Saturday 12 August 2006 17:41, david singleton wrote:
> Here's the powerop-x86-centrino.patch.

Please do not propagate the hard coded tables and control word formats
from speedstep-centrino.c.  The ACPI tables are preferred,
and the hard-coded tables and control word formats in speedstep-centrino
are known to be inaccurate and unsupported.

Also, there needs to be a mechanism where the tables can change at run-time.

-Len

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2006-08-16 15:02 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-08-08 18:12 Dynanic On-The-Fly Operating points for PowerOP David Singleton
2006-08-09 21:17 ` Matthew Locke
2006-08-10  4:39   ` david singleton
2006-08-10  7:44     ` Matthew Locke
2006-08-12  8:07       ` Vitaly Wool
2006-08-12 18:12         ` david singleton
2006-08-12 21:32         ` david singleton
2006-08-12 21:39         ` david singleton
2006-08-12 21:40         ` david singleton
2006-08-12 21:41         ` david singleton
2006-08-16 15:02           ` Len Brown
2006-08-12 23:14         ` Matthew Locke
2006-08-13  2:25           ` Preece Scott-PREECE
2006-08-14  3:37           ` david singleton
2006-08-15 19:44 ` Pavel Machek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox