[linux-yocto] [PATCH] MIPS: OCTEON: Add sysfs support for CPU power throttling.
Chandrakala Chavva
cchavva.cavm at gmail.com
Thu Jan 29 07:32:47 PST 2015
From: Abhishek Paliwal <abhishek.paliwal at aricent.com>
From: David Daney <david.daney at cavium.com>
Signed-off-by: David Daney <david.daney at cavium.com>
Signed-off-by: Abhishek Paliwal <abhishek.paliwal at 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 <pjohn at mvista.com>
+ * Credits: This driver is derived from Dmitriy Zavin's (dmitriyz at 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
More information about the linux-yocto
mailing list