From: Abhishek Paliwal <abhishek.pali...@aricent.com>

From: David Daney <david.da...@cavium.com>

Signed-off-by: David Daney <david.da...@cavium.com>
Signed-off-by: Abhishek Paliwal <abhishek.pali...@aricent.com>
---
 arch/mips/cavium-octeon/Makefile                |   1 +
 arch/mips/cavium-octeon/octeon-power-throttle.c | 394 ++++++++++++++++++++++++
 2 files changed, 395 insertions(+)
 create mode 100644 arch/mips/cavium-octeon/octeon-power-throttle.c

diff --git a/arch/mips/cavium-octeon/Makefile b/arch/mips/cavium-octeon/Makefile
index e16fd26..134a718 100644
--- a/arch/mips/cavium-octeon/Makefile
+++ b/arch/mips/cavium-octeon/Makefile
@@ -20,6 +20,7 @@ obj-y += executive/
 obj-$(CONFIG_MTD)                    += flash_setup.o
 obj-$(CONFIG_SMP)                    += smp.o
 obj-$(CONFIG_OCTEON_ILM)             += oct_ilm.o
+obj-$(CONFIG_SYSFS)                   += octeon-power-throttle.o
 obj-$(CONFIG_OCTEON_ERROR_INJECTOR)    += octeon-error-injector.o
 
 DTS_FILES = octeon_3xxx.dts octeon_68xx.dts
diff --git a/arch/mips/cavium-octeon/octeon-power-throttle.c 
b/arch/mips/cavium-octeon/octeon-power-throttle.c
new file mode 100644
index 0000000..6bd201c
--- /dev/null
+++ b/arch/mips/cavium-octeon/octeon-power-throttle.c
@@ -0,0 +1,394 @@
+/*
+ * octeon-power-throttle.c - interface for controlling power
+ * throttling on OCTEON II based platforms (6xxx and above).  OCTEON II
+ * supports dynamic power control which aids to cut down power
+ * consumption. The code exposes a "percentage" power throttling
+ * limiter by means of /sys interface for each available cpu. Setting
+ * this value to 0 will set power consumption to a minimum as it will
+ * only execute a couple instructions every PERIOD as set in the
+ * PowThrottle register.  If set to 100% for that particular cpu, it
+ * may consume maximum power.
+ *
+ * Copyright (C) 2012-2013 Cavium, Inc.
+ *
+ * Copyright (C) 2012 MontaVista LLC.
+ * Author: Philby John <pj...@mvista.com>
+ * Credits: This driver is derived from Dmitriy Zavin's (dmitr...@google.com)
+ * thermal throttle event support code.
+ */
+#include <linux/kernel.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/cpu.h>
+#include <linux/moduleparam.h>
+
+#include <asm/byteorder.h>
+#include <asm/octeon/octeon.h>
+
+union octeon_power_throttle_bits {
+       u64 raw;
+       struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+               u64 maxpow:8;           /* 63:56 */
+               u64 powe:8;             /* 55:48 */
+               u64 thrott:8;           /* 47:40 */
+               u64 hrmpowadj:8;        /* 39:32 reserved in cn63XX */
+               u64 reserved:3;         /* 31:29 */
+               u64 ovrrd:1;            /* 28  reserved in cn63XX */
+               u64 distag:1;           /* 27 */
+               u64 period:3;           /* 26:24 */
+               u64 powlim:8;           /* 23:16 */
+               u64 maxthr:8;           /* 15:8 */
+               u64 minthr:8;           /* 7:0 */
+#else
+               u64 minthr:8;
+               u64 maxthr:8;
+               u64 powlim:8;
+               u64 period:3;
+               u64 distag:1;
+               u64 ovrrd:1;
+               u64 reserved:3;
+               u64 hrmpowadj:8;
+               u64 thrott:8;
+               u64 powe:8;
+               u64 maxpow:8;
+#endif
+       } s;
+};
+
+/*
+ * Boot-time power limit as percentage,
+ * settable by bootparam: octeon_power_throttle.start=85
+ * Useful for situations where full-throttle boot would exceed power budget.
+ * Individual cores' power can be throttled up/down after boot.
+ * Default of -1 retains reset/bootloader powlim setting.
+ */
+static long boot_powlim = -1;
+module_param_named(start, boot_powlim, long, 0444);
+
+/* IPI calls to ask target CPU to access own registers ... */
+static inline void read_my_power_throttle(void *info)
+{
+       *(u64*)info = __read_64bit_c0_register($11, 6);
+}
+
+static inline void write_my_power_throttle(void *info)
+{
+       __write_64bit_c0_register($11, 6, *(u64*)info);
+}
+
+/*
+ * Read/Write POW_THROTTLE.
+ */
+static int throttle_op(int cpu,
+       union octeon_power_throttle_bits *r, bool write)
+{
+       int err =
+       smp_call_function_single(cpu, 
+               (write ? write_my_power_throttle
+                      : read_my_power_throttle),
+               r, 1);
+       return err;
+}
+
+/* get default max power, unscaled */
+static int get_powbase(union octeon_power_throttle_bits r)
+{
+       int lim = r.s.maxpow;
+       int adj = r.s.hrmpowadj;
+
+       if (!OCTEON_IS_MODEL(OCTEON_CN63XX))
+               lim -= adj;
+
+       return lim;
+}
+
+/*
+ * Throttle given core's power
+ */
+static void octeon_power_throttle_init_cpu(int cpu)
+{
+       union octeon_power_throttle_bits r;
+
+       if (throttle_op(cpu, &r, false))
+               return;
+
+       if (cpu == 0)
+               pr_debug("old power_throttle %llx\n",
+                       r.raw);
+
+       r.s.ovrrd = 0;          /* MBZ */
+       r.s.distag = 0;         /* MBZ */
+       r.s.period = 2;         /* 256 cycles */
+       r.s.minthr = 0;
+       r.s.maxthr = 0xff;
+
+       /* limit average power to boot_powlim% of max power */
+       if (boot_powlim >= 0)
+               r.s.powlim = (r.s.maxpow * boot_powlim) / 100;
+       else
+               r.s.powlim = get_powbase(r);
+
+       throttle_op(cpu, &r, true);
+}
+
+/* scale a throttle value as percentage of max power */
+static int scaled(union octeon_power_throttle_bits r, int val)
+{
+       int base = r.s.maxpow;
+
+       if (base <= 0)
+               return 100;
+       return ((val * 100) / base);
+}
+
+/*
+ * Set the POWLIM field as percentage% of the MAXPOW field in r.
+ */
+static int set_powlim(union octeon_power_throttle_bits *r, unsigned long 
percentage)
+{
+       int maxpow = r->s.maxpow;       /* max with override */
+       int base = get_powbase(*r);     /* max without override */
+       int newlim;
+       int ret = 0;
+
+       if (percentage < 0)
+               percentage = 0;
+
+       newlim = (maxpow * percentage) / 100;
+
+       if (newlim > maxpow)
+               newlim = maxpow;
+       if (newlim > base && !r->s.ovrrd)
+               newlim = base;
+
+       r->s.powlim = newlim;
+
+       return ret;
+}
+
+/* read actor for all throttle attributes */
+static ssize_t show(
+       struct device *dev,
+       struct device_attribute *attr,
+       char *buf)
+{
+       union octeon_power_throttle_bits r;
+       unsigned int cpu = dev->id;
+       int ret = -EBUSY;
+
+       get_online_cpus();
+       if (!cpu_online(cpu))
+               goto bye;
+       ret = throttle_op(cpu, &r, false);
+       if (ret)
+               goto bye;
+
+       switch (attr->attr.name[0]) {
+               int maxt;
+
+       case 'p': /* percent */
+               ret = sprintf(buf, "%d\n", scaled(r, r.s.powlim));
+               break;
+
+       case 'd': /* default */
+               ret = sprintf(buf, "%d\n", scaled(r, get_powbase(r)));
+               break;
+
+       case 'o': /* override */
+               ret = sprintf(buf, "%d\n",
+                       OCTEON_IS_MODEL(OCTEON_CN63XX) ? 0 : r.s.ovrrd);
+               break;
+
+       case 'c': /* cycles */
+               ret = sprintf(buf, "%d\n", (1024 >> r.s.period));
+               break;
+
+       case 'm': /* maxthr/minthr */
+               /* this name[0] "perfect hash" just broke ... */
+               maxt = (attr->attr.name[2] == 'x');
+               ret = sprintf(buf, "%d\n", maxt ? r.s.maxthr : r.s.minthr);
+               break;
+
+       case 's':
+               ret = sprintf(buf,
+                       "recent power:         %d\n"
+                       "recent throttle:      %d\n"
+                       "power limit:          %d%% %d\n"
+                       "default limit:        %d%% %d\n"
+                       "boot_powlim:          %ld%%\n"
+                       "adjustment cycles:    %d\n"
+                       "throttle_range:       %d..%d\n"
+                       "allow override:       %c\n"
+                       "raw:                  %llx\n",
+                       r.s.powe,
+                       r.s.thrott,
+                       scaled(r, r.s.powlim), r.s.powlim,
+                       scaled(r, get_powbase(r)), get_powbase(r),
+                       (boot_powlim >= 0
+                               ? boot_powlim
+                               : (get_powbase(r) * 100) / r.s.maxpow),
+                       (1024 >> r.s.period),
+                       r.s.minthr, r.s.maxthr,
+                       "NY"[r.s.ovrrd],
+                       r.raw);
+               break;
+
+       default:
+               ret = -ENXIO;
+               break;
+       }
+
+bye:
+       put_online_cpus();
+
+       return (ssize_t) ret;
+}
+
+/*
+ * write actor for all writeable throttle attributes.
+ * Generally take a single decimal input,
+ * but percentage allows 'd...' to reset to base-power default.
+ */
+static ssize_t store(
+       struct device *dev,
+       struct device_attribute *attr,
+       const char *buf,
+       size_t size)
+{
+       unsigned int cpu = dev->id;
+       unsigned long val = 0;
+       union octeon_power_throttle_bits r;
+       int error = 0;
+       bool restore_default_powlim =
+               (buf[0] == 'd' && attr->attr.name[0] == 'p');
+       
+       if (!restore_default_powlim)
+               error = kstrtoul(buf, 0, &val);
+
+       if (error)
+               return error;
+
+       get_online_cpus();
+       error = -EBUSY;
+       if (!cpu_online(cpu))
+               goto bye;
+       error = throttle_op(cpu, &r, false);
+       if (error)
+               goto bye;
+
+       switch (attr->attr.name[0]) {
+               int maxt;
+
+       case 'p': /* percent */
+               if (restore_default_powlim)
+                       val = get_powbase(r);
+               error = set_powlim(&r, val);
+               break;
+
+       case 'o': /* override */
+               if (val < 0 || val > 1 ||
+                   OCTEON_IS_MODEL(OCTEON_CN63XX)) {
+                       error = -EINVAL;
+               } else {
+                       if (r.s.ovrrd && r.s.powlim > get_powbase(r))
+                               r.s.powlim = get_powbase(r);
+                       r.s.ovrrd = val;
+               }
+               break;
+
+       case 'c': /* cycles */
+               /* set throttle period, either cycles or 0..3 encoding */
+               if (val >= 0 && val <= 3)
+                       r.s.period = val;
+               else if (val >= 1024)
+                       r.s.period = 0;
+               else if (val >= 512)
+                       r.s.period = 1;
+               else if (val >= 256)
+                       r.s.period = 2;
+               else if (val >= 128)
+                       r.s.period = 3;
+               else
+                       error = -EINVAL;
+               break;
+
+       case 'm': /* maxthr/minthr */
+               /* this name[0] "perfect hash" just broke ... */
+               maxt = (attr->attr.name[2] == 'x');
+               if (maxt)
+                       r.s.maxthr = val;
+               else
+                       r.s.minthr = val;
+               break;
+
+       default:
+               error = -EINVAL;
+               break;
+       }
+
+       if (!error)
+               error = throttle_op(cpu, &r, true);
+
+bye:
+       put_online_cpus();
+
+       if (error)
+               return error;
+       return size;
+}
+
+static DEVICE_ATTR(percentage, S_IRUGO | S_IWUSR, show, store); 
+static DEVICE_ATTR(override, S_IRUGO | S_IWUSR, show, store); 
+static DEVICE_ATTR(cycles, S_IRUGO | S_IWUSR, show, store); 
+static DEVICE_ATTR(maxthr, S_IRUGO | S_IWUSR, show, store); 
+static DEVICE_ATTR(minthr, S_IRUGO | S_IWUSR, show, store); 
+static DEVICE_ATTR(default, S_IRUGO, show, NULL);
+static DEVICE_ATTR(state, S_IRUGO, show, NULL);
+
+static struct attribute *octeon_power_throttle_attrs[] = {
+       &dev_attr_percentage.attr,
+       &dev_attr_override.attr,
+       &dev_attr_cycles.attr,
+       &dev_attr_maxthr.attr,
+       &dev_attr_minthr.attr,
+       &dev_attr_default.attr,
+       &dev_attr_state.attr,
+       NULL
+};
+
+static struct attribute_group octeon_power_throttle_attr_group = {
+       .attrs  = octeon_power_throttle_attrs,
+       .name   = "power_throttle"
+};
+
+static __cpuinit int octeon_power_throttle_add_dev(struct device *dev)
+{
+       return sysfs_create_group(&dev->kobj,
+                                 &octeon_power_throttle_attr_group);
+}
+
+static __init int octeon_power_throttle_init(void)
+{
+       unsigned int cpu = 0;
+       int err = 0;
+
+       if (!(current_cpu_type() == CPU_CAVIUM_OCTEON2 ||
+               current_cpu_type() == CPU_CAVIUM_OCTEON3))
+               return 0;
+
+       get_online_cpus();
+       /* connect live CPUs to sysfs */
+       for_each_online_cpu(cpu) {
+               err = octeon_power_throttle_add_dev(get_cpu_device(cpu));
+               if (err) {
+                       pr_err("Error: octeon_power_throttle_add_dev() 
failed\n");
+                       break;
+               }
+               octeon_power_throttle_init_cpu(cpu);
+       }
+       put_online_cpus();
+       return err;
+}
+device_initcall(octeon_power_throttle_init);
-- 
1.8.1.4

-- 
_______________________________________________
linux-yocto mailing list
linux-yocto@yoctoproject.org
https://lists.yoctoproject.org/listinfo/linux-yocto

Reply via email to