Subject: cell: use pmi in cpufreq driver From: Christian Krafft The new PMI driver was added in order to support cpufreq on blades that require the frequency to be controlled by the service processor, so use it on those. Signed-off-by: Arnd Bergmann --- Index: linux-2.6/arch/powerpc/platforms/cell/cbe_cpufreq.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/cbe_cpufreq.c +++ linux-2.6/arch/powerpc/platforms/cell/cbe_cpufreq.c @@ -1,6 +1,22 @@ /* * cpufreq driver for the cell processor * + * If a PMI device node can be found in the devicetree, + * the driver will request a frequency change from the + * BMC via PMI. This will lower the latency a little, + * but it will allow the BMC to override cpufreq. + * This will prevent the board from being shutdown in + * case of power failure. + * + * If no PMI support is available, the driver will access the + * slow-mode registers by itself. + * + * PMI is a way to communicate with the BMC via interrupts. + * Unlike IPMI it is bidirectional and has a low latency. + * + * At the moment this driver is using PMI exclusively. The relevant code + * should go into a seperate module, if another module needs PMI. + * * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 * * Author: Christian Krafft @@ -28,11 +44,62 @@ #include #include #include - +#include +#include #include "cbe_regs.h" + static DEFINE_MUTEX(cbe_switch_mutex); +static struct of_device *pmi_dev; + +static int get_pmode_pmi(int cpu) +{ + /* TODO: place code here */ + + return 0; +} + +static int set_pmode_pmi(int cpu, unsigned int pmode) +{ +#ifdef DEBUG + struct cbe_pmd_regs __iomem *pmd_regs; + u64 value, time; +#endif + int ret; + struct pmi_message *pmi_msg; + +#ifdef DEBUG + pmd_regs = cbe_get_cpu_pmd_regs(cpu); + time = (u64) get_cycles(); +#endif + + pmi_msg = kzalloc(sizeof(struct pmi_message), GFP_KERNEL); + pmi_msg->type = PMI_TYPE_FREQ_CHANGE; + pmi_msg->data1 = cpu; + pmi_msg->data2 = pmode; + + /*TODO: here comes the code that issues a PMI message */ + printk(KERN_INFO "setting slow %d mode via PMI to cpu %d\n", pmode, cpu); + pmi_send_message(pmi_dev, pmi_msg); + ret = 0; + +#ifdef DEBUG + /* wait until new pmode appears in status register */ + /* + do { + value = in_be64(&pmd_regs->pmsr) & 0x07; + } while (value == pmode); + */ + time = (u64) get_cycles() - time; /* actual cycles (not cpu cycles!) */ + time = 1000000000 * time / CLOCK_TICK_RATE; /* time in ns (10^-9) */ + pr_debug("had to wait %lu ns for a transition\n", time); +#endif + return ret; +} + +/* end of PMI stuff */ + /* the CBE supports an 8 step frequency scaling */ static struct cpufreq_frequency_table cbe_freqs[] = { @@ -64,11 +131,12 @@ static u64 MIC_Slow_Next_Timer_table[] = 0x00003FC000000000ull, }; + /* * hardware specific functions */ -static int get_pmode(int cpu) +static int get_pmode_reg(int cpu) { int ret; struct cbe_pmd_regs __iomem *pmd_regs; @@ -79,7 +147,7 @@ static int get_pmode(int cpu) return ret; } -static int set_pmode(int cpu, unsigned int pmode) +static int set_pmode_reg(int cpu, unsigned int pmode) { struct cbe_pmd_regs __iomem *pmd_regs; struct cbe_mic_tm_regs __iomem *mic_tm_regs; @@ -120,6 +188,29 @@ static int set_pmode(int cpu, unsigned i return 0; } +static int get_pmode(int cpu) +{ + if(pmi_dev) { + pr_debug("get pmode via PMI\n"); + return get_pmode_pmi(cpu); + } + + pr_debug("get pmode from reg\n"); + return get_pmode_reg(cpu); +} + + +static int set_pmode(int cpu, unsigned int slow_mode) { + if(pmi_dev) { + pr_debug("setting slow_mode via PMI\n"); + return set_pmode_pmi(cpu, slow_mode); + } + + pr_debug("setting slow_mode directly\n"); + return set_pmode_reg(cpu, slow_mode); +} + + /* * cpufreq functions */ @@ -227,17 +318,42 @@ static struct cpufreq_driver cbe_cpufreq .flags = CPUFREQ_CONST_LOOPS, }; +static void cbe_handle_pmi(struct of_device *dev, struct pmi_message *pmi_msg) { + pr_debug("cbe_handle_pmi: got called\n"); + + BUG_ON (pmi_msg->type == PMI_TYPE_FREQ_CHANGE); + /* TODO: add some code here */ +} + +static struct pmi_handler cbe_pmi_handler = { + .type = PMI_TYPE_FREQ_CHANGE, + .handle_pmi_message = cbe_handle_pmi, +}; + /* * module init and destoy */ static int __init cbe_cpufreq_init(void) { + struct device_node *np; + + /* register to PMI */ + np = of_find_node_by_type(NULL, "ibm,pmi"); + + pmi_dev = of_find_device_by_node(np); + + if (pmi_dev) + pmi_register_handler(pmi_dev, &cbe_pmi_handler); + return cpufreq_register_driver(&cbe_cpufreq_driver); } static void __exit cbe_cpufreq_exit(void) { + if(pmi_dev) + pmi_unregister_handler(pmi_dev, &cbe_pmi_handler); + cpufreq_unregister_driver(&cbe_cpufreq_driver); } Index: linux-2.6/arch/powerpc/configs/cell_defconfig =================================================================== --- linux-2.6.orig/arch/powerpc/configs/cell_defconfig +++ linux-2.6/arch/powerpc/configs/cell_defconfig @@ -147,6 +147,7 @@ CONFIG_PPC_RTAS=y # CONFIG_RTAS_ERROR_LOGGING is not set CONFIG_RTAS_PROC=y CONFIG_RTAS_FLASH=y +CONFIG_PPC_PMI=m CONFIG_MMIO_NVRAM=y # CONFIG_PPC_MPC106 is not set # CONFIG_PPC_970_NAP is not set