Re: Alternative Concept [Was: Re: [RFC] CPUFreq PowerOP integration, Intro 0/3]

Dominik Brodowski
2006-10-07T03:15:01Z

Hi,

Some very basic incomplete sketch of how some of my ideas might look in
code:

/*
* OMAP PM Core implementation by Eugeny S. Mints <eugeny.mints@gmail.com>,
* modified by Dominik Brodowski.
*
* Copyright (C) 2006, Nomad Global Solutions, Inc.
* Copyright (C) 2006 Dominik Brodowski
*
* Based on code by Todd Poynor, Matthew Locke, Dmitry Chigirev, and
* Bishop Brock.
*
* Copyright (C) 2002, 2004 MontaVista Software <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
*
*/

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

/* Number of colums in operating points table */
#define OP_VALUES 4

/* GENERIC PART BEGINS HERE */

struct op_entry {
unsigned int value[OP_VALUES];
struct op_entry *next;
};

int pm_core_verify_value(unsigned int what, unsigned int value,
struct op_entry *table,
int set_other(unsigned int, unsigned int))
{
int i, colum = -1;
struct op_entry *table_entry = table->next;

for (i=0; i<OP_VALUES; i++) {
if (table->value[i] == what)
colum = i;
}
if (colum == -1)
return -EINVAL;

while (table_entry) {
/* very easy algorithm at first: use the first
operating point which matches what we are looking
for
*/
if (table_entry->value[colum] == value)
goto found_it;

table_entry = table_entry->next;
};
return -EINVAL;

found_it:
for (i=0; i<OP_VALUES; i++) {
if (i==colum)
continue;
/* FIXME: error handling */
set_other(table->value[i], table_entry->value[i]);
}
return 0;
}

/* GENERIC PART ENDS HERE */

/* dummy */
static int set_cpu_vltg(unsigned int value)
{
return 0;
}

static int set_dpll(unsigned int value)
{
return 0;
}

static int set_cpu_freq(unsigned int value)
{
return 0;
}

enum {
CPU_VLTG = 0,
DPLL = 1,
CPU_FREQ = 2,
OPPOINT_NR = 3,
};

static int set_pm(unsigned int what, unsigned int value)
{
int ret = -ENODEV;

/* FIXME
pm_core_notify_prechange(what, value);
*/

switch (what) {
case CPU_VLTG:
/* FIXME: only set it if it is really different
* from current level */
ret = set_cpu_vltg(value);
break;
case DPLL:
ret = set_dpll(value);
break;
case CPU_FREQ:
ret = set_cpu_freq(value);
break;
case OPPOINT_NR:
/* ignore */
break;
}

/* FIXME
if (!ret)
pm_core_notify_postchange(what, value);
else
pm_core_notify_failed_change(what, value);
*/

return ret;
}

/* THE TABLE CONTAING PRE-DEFINED OPERATING POINTS */

/* FIXME: think of a better way to set up a static table */
struct op_entry omap_op_static_entry4 = {
.value = {2, 3, 2, 4},
};

struct op_entry omap_op_static_entry3 = {
.value = {1, 2, 2, 3},
.next = &omap_op_static_entry4,
};

struct op_entry omap_op_static_entry2 = {
.value = {2, 1, 1, 2},
.next = &omap_op_static_entry3,
};

struct op_entry omap_op_static_entry1 = {
.value = {1, 1, 1, 1},
.next = &omap_op_static_entry2,
};

struct op_entry omap_op_table = {
.value = {CPU_VLTG, DPLL, CPU_FREQ, OPPOINT_NR},
.next = &omap_op_static_entry1,
};

/* function being called by external interface. You can add
whatever userspace API on top of it you like -- configfs,
sysfs, module parameter, syscall, ... */

int omap_set(unsigned int what, unsigned int value)
{
int ret;

ret = pm_core_verify_value(what, value, &omap_op_table, set_pm);
if (ret)
return ret;

return set_pm(what, value);
}

/*
To set a specific operating point, let's say, oppoint 3, the
yet-to-be-defined interface needs to call:

omap_set(OPPOINT_NR, 3);

Setting up a cpufreq interaction is quite easy, as it basically only
needs to call

omap_set(CPU_FREQ, validated_target_freq);

You only need to make sure that you're not using in-kernel CPU
frequency scaling parallel to setting oppoints. But such a validation
should be trivial to add.
*/

IT COMPILES!!! SHIP IT! ;)

Thanks,
Dominik