Merge tag 'pm-merge' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[linux-block.git] / drivers / mfd / db8500-prcmu.c
index 3167bfdd13fa04011ffa231fde753c5cd4098d3a..29710565a08fca94ce90ac4f3f2feaba843276f1 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/mfd/abx500/ab8500.h>
 #include <linux/regulator/db8500-prcmu.h>
 #include <linux/regulator/machine.h>
+#include <linux/cpufreq.h>
 #include <asm/hardware/gic.h>
 #include <mach/hardware.h>
 #include <mach/irqs.h>
@@ -420,9 +421,6 @@ static struct {
 
 static atomic_t ac_wake_req_state = ATOMIC_INIT(0);
 
-/* Functions definition */
-static void compute_armss_rate(void);
-
 /* Spinlocks */
 static DEFINE_SPINLOCK(prcmu_lock);
 static DEFINE_SPINLOCK(clkout_lock);
@@ -1019,7 +1017,6 @@ int db8500_prcmu_set_arm_opp(u8 opp)
                (mb1_transfer.ack.arm_opp != opp))
                r = -EIO;
 
-       compute_armss_rate();
        mutex_unlock(&mb1_transfer.lock);
 
        return r;
@@ -1669,13 +1666,8 @@ static unsigned long clock_rate(u8 clock)
        else
                return 0;
 }
-static unsigned long latest_armss_rate;
-static unsigned long armss_rate(void)
-{
-       return latest_armss_rate;
-}
 
-static void compute_armss_rate(void)
+static unsigned long armss_rate(void)
 {
        u32 r;
        unsigned long rate;
@@ -1700,7 +1692,7 @@ static void compute_armss_rate(void)
                rate = pll_rate(PRCM_PLLARM_FREQ, ROOT_CLOCK_RATE, PLL_DIV);
        }
 
-       latest_armss_rate = rate;
+       return rate;
 }
 
 static unsigned long dsiclk_rate(u8 n)
@@ -1820,6 +1812,35 @@ static long round_clock_rate(u8 clock, unsigned long rate)
        return rounded_rate;
 }
 
+/* CPU FREQ table, may be changed due to if MAX_OPP is supported. */
+static struct cpufreq_frequency_table db8500_cpufreq_table[] = {
+       { .frequency = 200000, .index = ARM_EXTCLK,},
+       { .frequency = 400000, .index = ARM_50_OPP,},
+       { .frequency = 800000, .index = ARM_100_OPP,},
+       { .frequency = CPUFREQ_TABLE_END,}, /* To be used for MAX_OPP. */
+       { .frequency = CPUFREQ_TABLE_END,},
+};
+
+static long round_armss_rate(unsigned long rate)
+{
+       long freq = 0;
+       int i = 0;
+
+       /* cpufreq table frequencies is in KHz. */
+       rate = rate / 1000;
+
+       /* Find the corresponding arm opp from the cpufreq table. */
+       while (db8500_cpufreq_table[i].frequency != CPUFREQ_TABLE_END) {
+               freq = db8500_cpufreq_table[i].frequency;
+               if (freq == rate)
+                       break;
+               i++;
+       }
+
+       /* Return the last valid value, even if a match was not found. */
+       return freq * 1000;
+}
+
 #define MIN_PLL_VCO_RATE 600000000ULL
 #define MAX_PLL_VCO_RATE 1680640000ULL
 
@@ -1891,6 +1912,8 @@ long prcmu_round_clock_rate(u8 clock, unsigned long rate)
 {
        if (clock < PRCMU_NUM_REG_CLOCKS)
                return round_clock_rate(clock, rate);
+       else if (clock == PRCMU_ARMSS)
+               return round_armss_rate(rate);
        else if (clock == PRCMU_PLLDSI)
                return round_plldsi_rate(rate);
        else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK))
@@ -1950,6 +1973,27 @@ static void set_clock_rate(u8 clock, unsigned long rate)
        spin_unlock_irqrestore(&clk_mgt_lock, flags);
 }
 
+static int set_armss_rate(unsigned long rate)
+{
+       int i = 0;
+
+       /* cpufreq table frequencies is in KHz. */
+       rate = rate / 1000;
+
+       /* Find the corresponding arm opp from the cpufreq table. */
+       while (db8500_cpufreq_table[i].frequency != CPUFREQ_TABLE_END) {
+               if (db8500_cpufreq_table[i].frequency == rate)
+                       break;
+               i++;
+       }
+
+       if (db8500_cpufreq_table[i].frequency != rate)
+               return -EINVAL;
+
+       /* Set the new arm opp. */
+       return db8500_prcmu_set_arm_opp(db8500_cpufreq_table[i].index);
+}
+
 static int set_plldsi_rate(unsigned long rate)
 {
        unsigned long src_rate;
@@ -2030,6 +2074,8 @@ int prcmu_set_clock_rate(u8 clock, unsigned long rate)
 {
        if (clock < PRCMU_NUM_REG_CLOCKS)
                set_clock_rate(clock, rate);
+       else if (clock == PRCMU_ARMSS)
+               return set_armss_rate(rate);
        else if (clock == PRCMU_PLLDSI)
                return set_plldsi_rate(rate);
        else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK))
@@ -2697,9 +2743,15 @@ static struct irq_domain_ops db8500_irq_ops = {
 
 static int db8500_irq_init(struct device_node *np)
 {
-       db8500_irq_domain = irq_domain_add_legacy(
-               np, NUM_PRCMU_WAKEUPS, IRQ_PRCMU_BASE,
-               0, &db8500_irq_ops, NULL);
+       int irq_base = -1;
+
+       /* In the device tree case, just take some IRQs */
+       if (!np)
+               irq_base = IRQ_PRCMU_BASE;
+
+       db8500_irq_domain = irq_domain_add_simple(
+               np, NUM_PRCMU_WAKEUPS, irq_base,
+               &db8500_irq_ops, NULL);
 
        if (!db8500_irq_domain) {
                pr_err("Failed to create irqdomain\n");
@@ -2754,8 +2806,6 @@ void __init db8500_prcmu_early_init(void)
        init_completion(&mb5_transfer.work);
 
        INIT_WORK(&mb0_transfer.mask_work, prcmu_mask_work);
-
-       compute_armss_rate();
 }
 
 static void __init init_prcm_registers(void)
@@ -3020,6 +3070,8 @@ static struct mfd_cell db8500_prcmu_devs[] = {
        {
                .name = "cpufreq-u8500",
                .of_compatible = "stericsson,cpufreq-u8500",
+               .platform_data = &db8500_cpufreq_table,
+               .pdata_size = sizeof(db8500_cpufreq_table),
        },
        {
                .name = "ab8500-core",
@@ -3030,11 +3082,19 @@ static struct mfd_cell db8500_prcmu_devs[] = {
        },
 };
 
+static void db8500_prcmu_update_cpufreq(void)
+{
+       if (prcmu_has_arm_maxopp()) {
+               db8500_cpufreq_table[3].frequency = 1000000;
+               db8500_cpufreq_table[3].index = ARM_MAX_OPP;
+       }
+}
+
 /**
  * prcmu_fw_init - arch init call for the Linux PRCMU fw init logic
  *
  */
-static int __devinit db8500_prcmu_probe(struct platform_device *pdev)
+static int db8500_prcmu_probe(struct platform_device *pdev)
 {
        struct ab8500_platform_data *ab8500_platdata = pdev->dev.platform_data;
        struct device_node *np = pdev->dev.of_node;
@@ -3074,6 +3134,8 @@ static int __devinit db8500_prcmu_probe(struct platform_device *pdev)
        if (cpu_is_u8500v20_or_later())
                prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET);
 
+       db8500_prcmu_update_cpufreq();
+
        err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs,
                              ARRAY_SIZE(db8500_prcmu_devs), NULL, 0, NULL);
        if (err) {