[PATCH] ppc64: SMU based macs cpufreq support

CPU freq support using 970FX powertune facility for iMac G5 and SMU
based single CPU desktop.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
This commit is contained in:
Benjamin Herrenschmidt 2005-11-07 14:27:33 +11:00 committed by Paul Mackerras
parent a82765b6ee
commit 4350147a81
11 changed files with 572 additions and 25 deletions

View File

@ -404,6 +404,14 @@ config CPU_FREQ_PMAC
this currently includes some models of iBook & Titanium this currently includes some models of iBook & Titanium
PowerBook. PowerBook.
config CPU_FREQ_PMAC64
bool "Support for some Apple G5s"
depends on CPU_FREQ && PMAC_SMU && PPC64
select CPU_FREQ_TABLE
help
This adds support for frequency switching on Apple iMac G5,
and some of the more recent desktop G5 machines as well.
config PPC601_SYNC_FIX config PPC601_SYNC_FIX
bool "Workarounds for PPC601 bugs" bool "Workarounds for PPC601 bugs"
depends on 6xx && (PPC_PREP || PPC_PMAC) depends on 6xx && (PPC_PREP || PPC_PMAC)

View File

@ -603,6 +603,76 @@ _GLOBAL(real_writeb)
blr blr
#endif /* defined(CONFIG_PPC_PMAC) || defined(CONFIG_PPC_MAPLE) */ #endif /* defined(CONFIG_PPC_PMAC) || defined(CONFIG_PPC_MAPLE) */
/*
* SCOM access functions for 970 (FX only for now)
*
* unsigned long scom970_read(unsigned int address);
* void scom970_write(unsigned int address, unsigned long value);
*
* The address passed in is the 24 bits register address. This code
* is 970 specific and will not check the status bits, so you should
* know what you are doing.
*/
_GLOBAL(scom970_read)
/* interrupts off */
mfmsr r4
ori r0,r4,MSR_EE
xori r0,r0,MSR_EE
mtmsrd r0,1
/* rotate 24 bits SCOM address 8 bits left and mask out it's low 8 bits
* (including parity). On current CPUs they must be 0'd,
* and finally or in RW bit
*/
rlwinm r3,r3,8,0,15
ori r3,r3,0x8000
/* do the actual scom read */
sync
mtspr SPRN_SCOMC,r3
isync
mfspr r3,SPRN_SCOMD
isync
mfspr r0,SPRN_SCOMC
isync
/* XXX: fixup result on some buggy 970's (ouch ! we lost a bit, bah
* that's the best we can do). Not implemented yet as we don't use
* the scom on any of the bogus CPUs yet, but may have to be done
* ultimately
*/
/* restore interrupts */
mtmsrd r4,1
blr
_GLOBAL(scom970_write)
/* interrupts off */
mfmsr r5
ori r0,r5,MSR_EE
xori r0,r0,MSR_EE
mtmsrd r0,1
/* rotate 24 bits SCOM address 8 bits left and mask out it's low 8 bits
* (including parity). On current CPUs they must be 0'd.
*/
rlwinm r3,r3,8,0,15
sync
mtspr SPRN_SCOMD,r4 /* write data */
isync
mtspr SPRN_SCOMC,r3 /* write command */
isync
mfspr 3,SPRN_SCOMC
isync
/* restore interrupts */
mtmsrd r5,1
blr
/* /*
* Create a kernel thread * Create a kernel thread
* kernel_thread(fn, arg, flags) * kernel_thread(fn, arg, flags)

View File

@ -1,7 +1,8 @@
obj-y += pic.o setup.o time.o feature.o pci.o \ obj-y += pic.o setup.o time.o feature.o pci.o \
sleep.o low_i2c.o cache.o sleep.o low_i2c.o cache.o
obj-$(CONFIG_PMAC_BACKLIGHT) += backlight.o obj-$(CONFIG_PMAC_BACKLIGHT) += backlight.o
obj-$(CONFIG_CPU_FREQ_PMAC) += cpufreq.o obj-$(CONFIG_CPU_FREQ_PMAC) += cpufreq_32.o
obj-$(CONFIG_CPU_FREQ_PMAC64) += cpufreq_64.o
obj-$(CONFIG_NVRAM) += nvram.o obj-$(CONFIG_NVRAM) += nvram.o
# ppc64 pmac doesn't define CONFIG_NVRAM but needs nvram stuff # ppc64 pmac doesn't define CONFIG_NVRAM but needs nvram stuff
obj-$(CONFIG_PPC64) += nvram.o obj-$(CONFIG_PPC64) += nvram.o

View File

@ -397,18 +397,16 @@ static int pmac_cpufreq_target( struct cpufreq_policy *policy,
unsigned int relation) unsigned int relation)
{ {
unsigned int newstate = 0; unsigned int newstate = 0;
int rc;
if (cpufreq_frequency_table_target(policy, pmac_cpu_freqs, if (cpufreq_frequency_table_target(policy, pmac_cpu_freqs,
target_freq, relation, &newstate)) target_freq, relation, &newstate))
return -EINVAL; return -EINVAL;
return do_set_cpu_speed(newstate, 1); rc = do_set_cpu_speed(newstate, 1);
}
unsigned int pmac_get_one_cpufreq(int i) ppc_proc_freq = cur_freq * 1000ul;
{ return rc;
/* Supports only one CPU for now */
return (i == 0) ? cur_freq : 0;
} }
static int pmac_cpufreq_cpu_init(struct cpufreq_policy *policy) static int pmac_cpufreq_cpu_init(struct cpufreq_policy *policy)
@ -464,7 +462,7 @@ static int pmac_cpufreq_resume(struct cpufreq_policy *policy)
/* If we resume, first check if we have a get() function */ /* If we resume, first check if we have a get() function */
if (get_speed_proc) if (get_speed_proc)
cur_freq = get_speed_proc(); cur_freq = get_speed_proc();
else else)
cur_freq = 0; cur_freq = 0;
/* We don't, hrm... we don't really know our speed here, best /* We don't, hrm... we don't really know our speed here, best
@ -474,6 +472,8 @@ static int pmac_cpufreq_resume(struct cpufreq_policy *policy)
do_set_cpu_speed(sleep_freq == low_freq ? do_set_cpu_speed(sleep_freq == low_freq ?
CPUFREQ_LOW : CPUFREQ_HIGH, 0); CPUFREQ_LOW : CPUFREQ_HIGH, 0);
ppc_proc_freq = cur_freq * 1000ul;
no_schedule = 0; no_schedule = 0;
return 0; return 0;
} }
@ -714,6 +714,7 @@ static int __init pmac_cpufreq_setup(void)
pmac_cpu_freqs[CPUFREQ_LOW].frequency = low_freq; pmac_cpu_freqs[CPUFREQ_LOW].frequency = low_freq;
pmac_cpu_freqs[CPUFREQ_HIGH].frequency = hi_freq; pmac_cpu_freqs[CPUFREQ_HIGH].frequency = hi_freq;
ppc_proc_freq = cur_freq * 1000ul;
printk(KERN_INFO "Registering PowerMac CPU frequency driver\n"); printk(KERN_INFO "Registering PowerMac CPU frequency driver\n");
printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Boot: %d Mhz\n", printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Boot: %d Mhz\n",

View File

@ -0,0 +1,323 @@
/*
* Copyright (C) 2002 - 2005 Benjamin Herrenschmidt <benh@kernel.crashing.org>
* and Markus Demleitner <msdemlei@cl.uni-heidelberg.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This driver adds basic cpufreq support for SMU & 970FX based G5 Macs,
* that is iMac G5 and latest single CPU desktop.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/cpufreq.h>
#include <linux/init.h>
#include <linux/completion.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/irq.h>
#include <asm/sections.h>
#include <asm/cputable.h>
#include <asm/time.h>
#include <asm/smu.h>
#undef DEBUG
#ifdef DEBUG
#define DBG(fmt...) printk(fmt)
#else
#define DBG(fmt...)
#endif
/* see 970FX user manual */
#define SCOM_PCR 0x0aa001 /* PCR scom addr */
#define PCR_HILO_SELECT 0x80000000U /* 1 = PCR, 0 = PCRH */
#define PCR_SPEED_FULL 0x00000000U /* 1:1 speed value */
#define PCR_SPEED_HALF 0x00020000U /* 1:2 speed value */
#define PCR_SPEED_QUARTER 0x00040000U /* 1:4 speed value */
#define PCR_SPEED_MASK 0x000e0000U /* speed mask */
#define PCR_SPEED_SHIFT 17
#define PCR_FREQ_REQ_VALID 0x00010000U /* freq request valid */
#define PCR_VOLT_REQ_VALID 0x00008000U /* volt request valid */
#define PCR_TARGET_TIME_MASK 0x00006000U /* target time */
#define PCR_STATLAT_MASK 0x00001f00U /* STATLAT value */
#define PCR_SNOOPLAT_MASK 0x000000f0U /* SNOOPLAT value */
#define PCR_SNOOPACC_MASK 0x0000000fU /* SNOOPACC value */
#define SCOM_PSR 0x408001 /* PSR scom addr */
/* warning: PSR is a 64 bits register */
#define PSR_CMD_RECEIVED 0x2000000000000000U /* command received */
#define PSR_CMD_COMPLETED 0x1000000000000000U /* command completed */
#define PSR_CUR_SPEED_MASK 0x0300000000000000U /* current speed */
#define PSR_CUR_SPEED_SHIFT (56)
/*
* The G5 only supports two frequencies (Quarter speed is not supported)
*/
#define CPUFREQ_HIGH 0
#define CPUFREQ_LOW 1
static struct cpufreq_frequency_table g5_cpu_freqs[] = {
{CPUFREQ_HIGH, 0},
{CPUFREQ_LOW, 0},
{0, CPUFREQ_TABLE_END},
};
static struct freq_attr* g5_cpu_freqs_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
NULL,
};
/* Power mode data is an array of the 32 bits PCR values to use for
* the various frequencies, retreived from the device-tree
*/
static u32 *g5_pmode_data;
static int g5_pmode_max;
static int g5_pmode_cur;
static DECLARE_MUTEX(g5_switch_mutex);
static struct smu_sdbp_fvt *g5_fvt_table; /* table of op. points */
static int g5_fvt_count; /* number of op. points */
static int g5_fvt_cur; /* current op. point */
/* ----------------- real hardware interface */
static void g5_switch_volt(int speed_mode)
{
struct smu_simple_cmd cmd;
DECLARE_COMPLETION(comp);
smu_queue_simple(&cmd, SMU_CMD_POWER_COMMAND, 8, smu_done_complete,
&comp, 'V', 'S', 'L', 'E', 'W',
0xff, g5_fvt_cur+1, speed_mode);
wait_for_completion(&comp);
}
static int g5_switch_freq(int speed_mode)
{
struct cpufreq_freqs freqs;
int to;
if (g5_pmode_cur == speed_mode)
return 0;
down(&g5_switch_mutex);
freqs.old = g5_cpu_freqs[g5_pmode_cur].frequency;
freqs.new = g5_cpu_freqs[speed_mode].frequency;
freqs.cpu = 0;
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
/* If frequency is going up, first ramp up the voltage */
if (speed_mode < g5_pmode_cur)
g5_switch_volt(speed_mode);
/* Clear PCR high */
scom970_write(SCOM_PCR, 0);
/* Clear PCR low */
scom970_write(SCOM_PCR, PCR_HILO_SELECT | 0);
/* Set PCR low */
scom970_write(SCOM_PCR, PCR_HILO_SELECT |
g5_pmode_data[speed_mode]);
/* Wait for completion */
for (to = 0; to < 10; to++) {
unsigned long psr = scom970_read(SCOM_PSR);
if ((psr & PSR_CMD_RECEIVED) == 0 &&
(((psr >> PSR_CUR_SPEED_SHIFT) ^
(g5_pmode_data[speed_mode] >> PCR_SPEED_SHIFT)) & 0x3)
== 0)
break;
if (psr & PSR_CMD_COMPLETED)
break;
udelay(100);
}
/* If frequency is going down, last ramp the voltage */
if (speed_mode > g5_pmode_cur)
g5_switch_volt(speed_mode);
g5_pmode_cur = speed_mode;
ppc_proc_freq = g5_cpu_freqs[speed_mode].frequency * 1000ul;
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
up(&g5_switch_mutex);
return 0;
}
static int g5_query_freq(void)
{
unsigned long psr = scom970_read(SCOM_PSR);
int i;
for (i = 0; i <= g5_pmode_max; i++)
if ((((psr >> PSR_CUR_SPEED_SHIFT) ^
(g5_pmode_data[i] >> PCR_SPEED_SHIFT)) & 0x3) == 0)
break;
return i;
}
/* ----------------- cpufreq bookkeeping */
static int g5_cpufreq_verify(struct cpufreq_policy *policy)
{
return cpufreq_frequency_table_verify(policy, g5_cpu_freqs);
}
static int g5_cpufreq_target(struct cpufreq_policy *policy,
unsigned int target_freq, unsigned int relation)
{
unsigned int newstate = 0;
if (cpufreq_frequency_table_target(policy, g5_cpu_freqs,
target_freq, relation, &newstate))
return -EINVAL;
return g5_switch_freq(newstate);
}
static unsigned int g5_cpufreq_get_speed(unsigned int cpu)
{
return g5_cpu_freqs[g5_pmode_cur].frequency;
}
static int g5_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
if (policy->cpu != 0)
return -ENODEV;
policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
policy->cur = g5_cpu_freqs[g5_query_freq()].frequency;
cpufreq_frequency_table_get_attr(g5_cpu_freqs, policy->cpu);
return cpufreq_frequency_table_cpuinfo(policy,
g5_cpu_freqs);
}
static struct cpufreq_driver g5_cpufreq_driver = {
.name = "powermac",
.owner = THIS_MODULE,
.flags = CPUFREQ_CONST_LOOPS,
.init = g5_cpufreq_cpu_init,
.verify = g5_cpufreq_verify,
.target = g5_cpufreq_target,
.get = g5_cpufreq_get_speed,
.attr = g5_cpu_freqs_attr,
};
static int __init g5_cpufreq_init(void)
{
struct device_node *cpunode;
unsigned int psize, ssize;
struct smu_sdbp_header *shdr;
unsigned long max_freq;
u32 *valp;
int rc = -ENODEV;
/* Look for CPU and SMU nodes */
cpunode = of_find_node_by_type(NULL, "cpu");
if (!cpunode) {
DBG("No CPU node !\n");
return -ENODEV;
}
/* Check 970FX for now */
valp = (u32 *)get_property(cpunode, "cpu-version", NULL);
if (!valp) {
DBG("No cpu-version property !\n");
goto bail_noprops;
}
if (((*valp) >> 16) != 0x3c) {
DBG("Wrong CPU version: %08x\n", *valp);
goto bail_noprops;
}
/* Look for the powertune data in the device-tree */
g5_pmode_data = (u32 *)get_property(cpunode, "power-mode-data",&psize);
if (!g5_pmode_data) {
DBG("No power-mode-data !\n");
goto bail_noprops;
}
g5_pmode_max = psize / sizeof(u32) - 1;
/* Look for the FVT table */
shdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL);
if (!shdr)
goto bail_noprops;
g5_fvt_table = (struct smu_sdbp_fvt *)&shdr[1];
ssize = (shdr->len * sizeof(u32)) - sizeof(struct smu_sdbp_header);
g5_fvt_count = ssize / sizeof(struct smu_sdbp_fvt);
g5_fvt_cur = 0;
/* Sanity checking */
if (g5_fvt_count < 1 || g5_pmode_max < 1)
goto bail_noprops;
/*
* From what I see, clock-frequency is always the maximal frequency.
* The current driver can not slew sysclk yet, so we really only deal
* with powertune steps for now. We also only implement full freq and
* half freq in this version. So far, I haven't yet seen a machine
* supporting anything else.
*/
valp = (u32 *)get_property(cpunode, "clock-frequency", NULL);
if (!valp)
return -ENODEV;
max_freq = (*valp)/1000;
g5_cpu_freqs[0].frequency = max_freq;
g5_cpu_freqs[1].frequency = max_freq/2;
/* Check current frequency */
g5_pmode_cur = g5_query_freq();
if (g5_pmode_cur > 1)
/* We don't support anything but 1:1 and 1:2, fixup ... */
g5_pmode_cur = 1;
/* Force apply current frequency to make sure everything is in
* sync (voltage is right for example). Firmware may leave us with
* a strange setting ...
*/
g5_switch_freq(g5_pmode_cur);
printk(KERN_INFO "Registering G5 CPU frequency driver\n");
printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Cur: %d MHz\n",
g5_cpu_freqs[1].frequency/1000,
g5_cpu_freqs[0].frequency/1000,
g5_cpu_freqs[g5_pmode_cur].frequency/1000);
rc = cpufreq_register_driver(&g5_cpufreq_driver);
/* We keep the CPU node on hold... hopefully, Apple G5 don't have
* hotplug CPU with a dynamic device-tree ...
*/
return rc;
bail_noprops:
of_node_put(cpunode);
return rc;
}
module_init(g5_cpufreq_init);
MODULE_LICENSE("GPL");

View File

@ -193,18 +193,6 @@ static void pmac_show_cpuinfo(struct seq_file *m)
pmac_newworld ? "NewWorld" : "OldWorld"); pmac_newworld ? "NewWorld" : "OldWorld");
} }
static void pmac_show_percpuinfo(struct seq_file *m, int i)
{
#ifdef CONFIG_CPU_FREQ_PMAC
extern unsigned int pmac_get_one_cpufreq(int i);
unsigned int freq = pmac_get_one_cpufreq(i);
if (freq != 0) {
seq_printf(m, "clock\t\t: %dMHz\n", freq/1000);
return;
}
#endif /* CONFIG_CPU_FREQ_PMAC */
}
#ifndef CONFIG_ADB_CUDA #ifndef CONFIG_ADB_CUDA
int find_via_cuda(void) int find_via_cuda(void)
{ {
@ -767,7 +755,6 @@ struct machdep_calls __initdata pmac_md = {
.setup_arch = pmac_setup_arch, .setup_arch = pmac_setup_arch,
.init_early = pmac_init_early, .init_early = pmac_init_early,
.show_cpuinfo = pmac_show_cpuinfo, .show_cpuinfo = pmac_show_cpuinfo,
.show_percpuinfo = pmac_show_percpuinfo,
.init_IRQ = pmac_pic_init, .init_IRQ = pmac_pic_init,
.get_irq = mpic_get_irq, /* changed later */ .get_irq = mpic_get_irq, /* changed later */
.pcibios_fixup = pmac_pcibios_fixup, .pcibios_fixup = pmac_pcibios_fixup,

View File

@ -173,6 +173,16 @@ config KEXEC
support. As of this writing the exact hardware interface is support. As of this writing the exact hardware interface is
strongly in flux, so no good recommendation can be made. strongly in flux, so no good recommendation can be made.
source "drivers/cpufreq/Kconfig"
config CPU_FREQ_PMAC64
bool "Support for some Apple G5s"
depends on CPU_FREQ && PMAC_SMU && PPC64
select CPU_FREQ_TABLE
help
This adds support for frequency switching on Apple iMac G5,
and some of the more recent desktop G5 machines as well.
config IBMVIO config IBMVIO
depends on PPC_PSERIES || PPC_ISERIES depends on PPC_PSERIES || PPC_ISERIES
bool bool

View File

@ -560,7 +560,7 @@ _GLOBAL(real_readb)
isync isync
blr blr
/* /*
* Do an IO access in real mode * Do an IO access in real mode
*/ */
_GLOBAL(real_writeb) _GLOBAL(real_writeb)
@ -592,6 +592,76 @@ _GLOBAL(real_writeb)
blr blr
#endif /* defined(CONFIG_PPC_PMAC) || defined(CONFIG_PPC_MAPLE) */ #endif /* defined(CONFIG_PPC_PMAC) || defined(CONFIG_PPC_MAPLE) */
/*
* SCOM access functions for 970 (FX only for now)
*
* unsigned long scom970_read(unsigned int address);
* void scom970_write(unsigned int address, unsigned long value);
*
* The address passed in is the 24 bits register address. This code
* is 970 specific and will not check the status bits, so you should
* know what you are doing.
*/
_GLOBAL(scom970_read)
/* interrupts off */
mfmsr r4
ori r0,r4,MSR_EE
xori r0,r0,MSR_EE
mtmsrd r0,1
/* rotate 24 bits SCOM address 8 bits left and mask out it's low 8 bits
* (including parity). On current CPUs they must be 0'd,
* and finally or in RW bit
*/
rlwinm r3,r3,8,0,15
ori r3,r3,0x8000
/* do the actual scom read */
sync
mtspr SPRN_SCOMC,r3
isync
mfspr r3,SPRN_SCOMD
isync
mfspr r0,SPRN_SCOMC
isync
/* XXX: fixup result on some buggy 970's (ouch ! we lost a bit, bah
* that's the best we can do). Not implemented yet as we don't use
* the scom on any of the bogus CPUs yet, but may have to be done
* ultimately
*/
/* restore interrupts */
mtmsrd r4,1
blr
_GLOBAL(scom970_write)
/* interrupts off */
mfmsr r5
ori r0,r5,MSR_EE
xori r0,r0,MSR_EE
mtmsrd r0,1
/* rotate 24 bits SCOM address 8 bits left and mask out it's low 8 bits
* (including parity). On current CPUs they must be 0'd.
*/
rlwinm r3,r3,8,0,15
sync
mtspr SPRN_SCOMD,r4 /* write data */
isync
mtspr SPRN_SCOMC,r3 /* write command */
isync
mfspr 3,SPRN_SCOMC
isync
/* restore interrupts */
mtmsrd r5,1
blr
/* /*
* Create a kernel thread * Create a kernel thread
* kernel_thread(fn, arg, flags) * kernel_thread(fn, arg, flags)

View File

@ -845,6 +845,18 @@ int smu_queue_i2c(struct smu_i2c_cmd *cmd)
return 0; return 0;
} }
struct smu_sdbp_header *smu_get_sdb_partition(int id, unsigned int *size)
{
char pname[32];
if (!smu)
return NULL;
sprintf(pname, "sdb-partition-%02x", id);
return (struct smu_sdbp_header *)get_property(smu->of_node,
pname, size);
}
EXPORT_SYMBOL(smu_get_sdb_partition);
/* /*

View File

@ -396,6 +396,9 @@
#define SPRN_VRSAVE 0x100 /* Vector Register Save Register */ #define SPRN_VRSAVE 0x100 /* Vector Register Save Register */
#define SPRN_XER 0x001 /* Fixed Point Exception Register */ #define SPRN_XER 0x001 /* Fixed Point Exception Register */
#define SPRN_SCOMC 0x114 /* SCOM Access Control */
#define SPRN_SCOMD 0x115 /* SCOM Access DATA */
/* Performance monitor SPRs */ /* Performance monitor SPRs */
#ifdef CONFIG_PPC64 #ifdef CONFIG_PPC64
#define SPRN_MMCR0 795 #define SPRN_MMCR0 795
@ -594,7 +597,11 @@ static inline void ppc64_runlatch_off(void)
mtspr(SPRN_CTRLT, ctrl); mtspr(SPRN_CTRLT, ctrl);
} }
} }
#endif
extern unsigned long scom970_read(unsigned int address);
extern void scom970_write(unsigned int address, unsigned long value);
#endif /* CONFIG_PPC64 */
#define __get_SP() ({unsigned long sp; \ #define __get_SP() ({unsigned long sp; \
asm volatile("mr %0,1": "=r" (sp)); sp;}) asm volatile("mr %0,1": "=r" (sp)); sp;})

View File

@ -144,7 +144,11 @@
* - lenght 8 ("VSLEWxyz") has 3 additional bytes appended, and is * - lenght 8 ("VSLEWxyz") has 3 additional bytes appended, and is
* used to set the voltage slewing point. The SMU replies with "DONE" * used to set the voltage slewing point. The SMU replies with "DONE"
* I yet have to figure out their exact meaning of those 3 bytes in * I yet have to figure out their exact meaning of those 3 bytes in
* both cases. * both cases. They seem to be:
* x = processor mask
* y = op. point index
* z = processor freq. step index
* I haven't yet decyphered result codes
* *
*/ */
#define SMU_CMD_POWER_COMMAND 0xaa #define SMU_CMD_POWER_COMMAND 0xaa
@ -333,6 +337,60 @@ extern int smu_queue_i2c(struct smu_i2c_cmd *cmd);
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
/*
* - SMU "sdb" partitions informations -
*/
/*
* Partition header format
*/
struct smu_sdbp_header {
__u8 id;
__u8 len;
__u8 version;
__u8 flags;
};
/*
* 32 bits integers are usually encoded with 2x16 bits swapped,
* this demangles them
*/
#define SMU_U32_MIX(x) ((((x) << 16) & 0xffff0000u) | (((x) >> 16) & 0xffffu))
/* This is the definition of the SMU sdb-partition-0x12 table (called
* CPU F/V/T operating points in Darwin). The definition for all those
* SMU tables should be moved to some separate file
*/
#define SMU_SDB_FVT_ID 0x12
struct smu_sdbp_fvt {
__u32 sysclk; /* Base SysClk frequency in Hz for
* this operating point
*/
__u8 pad;
__u8 maxtemp; /* Max temp. supported by this
* operating point
*/
__u16 volts[3]; /* CPU core voltage for the 3
* PowerTune modes, a mode with
* 0V = not supported.
*/
};
#ifdef __KERNEL__
/*
* This returns the pointer to an SMU "sdb" partition data or NULL
* if not found. The data format is described below
*/
extern struct smu_sdbp_header *smu_get_sdb_partition(int id,
unsigned int *size);
#endif /* __KERNEL__ */
/* /*
* - Userland interface - * - Userland interface -
*/ */