Path: blob/master/arch/blackfin/mach-common/dpmc.c
10817 views
/*1* Copyright 2008 Analog Devices Inc.2*3* Licensed under the GPL-2 or later.4*/56#include <linux/cdev.h>7#include <linux/device.h>8#include <linux/errno.h>9#include <linux/fs.h>10#include <linux/kernel.h>11#include <linux/module.h>12#include <linux/platform_device.h>13#include <linux/types.h>14#include <linux/cpufreq.h>1516#include <asm/delay.h>17#include <asm/dpmc.h>1819#define DRIVER_NAME "bfin dpmc"2021struct bfin_dpmc_platform_data *pdata;2223/**24* bfin_set_vlev - Update VLEV field in VR_CTL Reg.25* Avoid BYPASS sequence26*/27static void bfin_set_vlev(unsigned int vlev)28{29unsigned pll_lcnt;3031pll_lcnt = bfin_read_PLL_LOCKCNT();3233bfin_write_PLL_LOCKCNT(1);34bfin_write_VR_CTL((bfin_read_VR_CTL() & ~VLEV) | vlev);35bfin_write_PLL_LOCKCNT(pll_lcnt);36}3738/**39* bfin_get_vlev - Get CPU specific VLEV from platform device data40*/41static unsigned int bfin_get_vlev(unsigned int freq)42{43int i;4445if (!pdata)46goto err_out;4748freq >>= 16;4950for (i = 0; i < pdata->tabsize; i++)51if (freq <= (pdata->tuple_tab[i] & 0xFFFF))52return pdata->tuple_tab[i] >> 16;5354err_out:55printk(KERN_WARNING "DPMC: No suitable CCLK VDDINT voltage pair found\n");56return VLEV_120;57}5859#ifdef CONFIG_CPU_FREQ60# ifdef CONFIG_SMP61static void bfin_idle_this_cpu(void *info)62{63unsigned long flags = 0;64unsigned long iwr0, iwr1, iwr2;65unsigned int cpu = smp_processor_id();6667local_irq_save_hw(flags);68bfin_iwr_set_sup0(&iwr0, &iwr1, &iwr2);6970platform_clear_ipi(cpu, IRQ_SUPPLE_0);71SSYNC();72asm("IDLE;");73bfin_iwr_restore(iwr0, iwr1, iwr2);7475local_irq_restore_hw(flags);76}7778static void bfin_idle_cpu(void)79{80smp_call_function(bfin_idle_this_cpu, NULL, 0);81}8283static void bfin_wakeup_cpu(void)84{85unsigned int cpu;86unsigned int this_cpu = smp_processor_id();87cpumask_t mask;8889cpumask_copy(&mask, cpu_online_mask);90cpumask_clear_cpu(this_cpu, &mask);91for_each_cpu(cpu, &mask)92platform_send_ipi_cpu(cpu, IRQ_SUPPLE_0);93}9495# else96static void bfin_idle_cpu(void) {}97static void bfin_wakeup_cpu(void) {}98# endif99100static int101vreg_cpufreq_notifier(struct notifier_block *nb, unsigned long val, void *data)102{103struct cpufreq_freqs *freq = data;104105if (freq->cpu != CPUFREQ_CPU)106return 0;107108if (val == CPUFREQ_PRECHANGE && freq->old < freq->new) {109bfin_idle_cpu();110bfin_set_vlev(bfin_get_vlev(freq->new));111udelay(pdata->vr_settling_time); /* Wait until Volatge settled */112bfin_wakeup_cpu();113} else if (val == CPUFREQ_POSTCHANGE && freq->old > freq->new) {114bfin_idle_cpu();115bfin_set_vlev(bfin_get_vlev(freq->new));116bfin_wakeup_cpu();117}118119return 0;120}121122static struct notifier_block vreg_cpufreq_notifier_block = {123.notifier_call = vreg_cpufreq_notifier124};125#endif /* CONFIG_CPU_FREQ */126127/**128* bfin_dpmc_probe -129*130*/131static int __devinit bfin_dpmc_probe(struct platform_device *pdev)132{133if (pdev->dev.platform_data)134pdata = pdev->dev.platform_data;135else136return -EINVAL;137138return cpufreq_register_notifier(&vreg_cpufreq_notifier_block,139CPUFREQ_TRANSITION_NOTIFIER);140}141142/**143* bfin_dpmc_remove -144*/145static int __devexit bfin_dpmc_remove(struct platform_device *pdev)146{147pdata = NULL;148return cpufreq_unregister_notifier(&vreg_cpufreq_notifier_block,149CPUFREQ_TRANSITION_NOTIFIER);150}151152struct platform_driver bfin_dpmc_device_driver = {153.probe = bfin_dpmc_probe,154.remove = __devexit_p(bfin_dpmc_remove),155.driver = {156.name = DRIVER_NAME,157}158};159160/**161* bfin_dpmc_init - Init driver162*/163static int __init bfin_dpmc_init(void)164{165return platform_driver_register(&bfin_dpmc_device_driver);166}167module_init(bfin_dpmc_init);168169/**170* bfin_dpmc_exit - break down driver171*/172static void __exit bfin_dpmc_exit(void)173{174platform_driver_unregister(&bfin_dpmc_device_driver);175}176module_exit(bfin_dpmc_exit);177178MODULE_AUTHOR("Michael Hennerich <[email protected]>");179MODULE_DESCRIPTION("cpu power management driver for Blackfin");180MODULE_LICENSE("GPL");181182183