| /* |
| * thermal support for the cell processor |
| * |
| * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 |
| * |
| * Author: Christian Krafft <krafft@de.ibm.com> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2, or (at your option) |
| * any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/sysdev.h> |
| #include <linux/kernel.h> |
| #include <linux/cpu.h> |
| #include <asm/spu.h> |
| #include <asm/io.h> |
| #include <asm/prom.h> |
| |
| #include "cbe_regs.h" |
| #include "spu_priv1_mmio.h" |
| |
| static struct cbe_pmd_regs __iomem *get_pmd_regs(struct sys_device *sysdev) |
| { |
| struct spu *spu; |
| |
| spu = container_of(sysdev, struct spu, sysdev); |
| |
| return cbe_get_pmd_regs(spu_devnode(spu)); |
| } |
| |
| /* returns the value for a given spu in a given register */ |
| static u8 spu_read_register_value(struct sys_device *sysdev, union spe_reg __iomem *reg) |
| { |
| const unsigned int *id; |
| union spe_reg value; |
| struct spu *spu; |
| |
| /* getting the id from the reg attribute will not work on future device-tree layouts |
| * in future we should store the id to the spu struct and use it here */ |
| spu = container_of(sysdev, struct spu, sysdev); |
| id = of_get_property(spu_devnode(spu), "reg", NULL); |
| value.val = in_be64(®->val); |
| |
| return value.spe[*id]; |
| } |
| |
| static ssize_t spu_show_temp(struct sys_device *sysdev, char *buf) |
| { |
| int value; |
| struct cbe_pmd_regs __iomem *pmd_regs; |
| |
| pmd_regs = get_pmd_regs(sysdev); |
| |
| value = spu_read_register_value(sysdev, &pmd_regs->ts_ctsr1); |
| /* clear all other bits */ |
| value &= 0x3F; |
| /* temp is stored in steps of 2 degrees */ |
| value *= 2; |
| /* base temp is 65 degrees */ |
| value += 65; |
| |
| return sprintf(buf, "%d\n", (int) value); |
| } |
| |
| static ssize_t ppe_show_temp(struct sys_device *sysdev, char *buf, int pos) |
| { |
| struct cbe_pmd_regs __iomem *pmd_regs; |
| u64 value; |
| |
| pmd_regs = cbe_get_cpu_pmd_regs(sysdev->id); |
| value = in_be64(&pmd_regs->ts_ctsr2); |
| |
| /* access the corresponding byte */ |
| value >>= pos; |
| /* clear all other bits */ |
| value &= 0x3F; |
| /* temp is stored in steps of 2 degrees */ |
| value *= 2; |
| /* base temp is 65 degrees */ |
| value += 65; |
| |
| return sprintf(buf, "%d\n", (int) value); |
| } |
| |
| |
| /* shows the temperature of the DTS on the PPE, |
| * located near the linear thermal sensor */ |
| static ssize_t ppe_show_temp0(struct sys_device *sysdev, char *buf) |
| { |
| return ppe_show_temp(sysdev, buf, 32); |
| } |
| |
| /* shows the temperature of the second DTS on the PPE */ |
| static ssize_t ppe_show_temp1(struct sys_device *sysdev, char *buf) |
| { |
| return ppe_show_temp(sysdev, buf, 0); |
| } |
| |
| static struct sysdev_attribute attr_spu_temperature = { |
| .attr = {.name = "temperature", .mode = 0400 }, |
| .show = spu_show_temp, |
| }; |
| |
| static struct attribute *spu_attributes[] = { |
| &attr_spu_temperature.attr, |
| NULL, |
| }; |
| |
| static struct attribute_group spu_attribute_group = { |
| .name = "thermal", |
| .attrs = spu_attributes, |
| }; |
| |
| static struct sysdev_attribute attr_ppe_temperature0 = { |
| .attr = {.name = "temperature0", .mode = 0400 }, |
| .show = ppe_show_temp0, |
| }; |
| |
| static struct sysdev_attribute attr_ppe_temperature1 = { |
| .attr = {.name = "temperature1", .mode = 0400 }, |
| .show = ppe_show_temp1, |
| }; |
| |
| static struct attribute *ppe_attributes[] = { |
| &attr_ppe_temperature0.attr, |
| &attr_ppe_temperature1.attr, |
| NULL, |
| }; |
| |
| static struct attribute_group ppe_attribute_group = { |
| .name = "thermal", |
| .attrs = ppe_attributes, |
| }; |
| |
| /* |
| * initialize throttling with default values |
| */ |
| static void __init init_default_values(void) |
| { |
| int cpu; |
| struct cbe_pmd_regs __iomem *pmd_regs; |
| struct sys_device *sysdev; |
| union ppe_spe_reg tpr; |
| union spe_reg str1; |
| u64 str2; |
| union spe_reg cr1; |
| u64 cr2; |
| |
| /* TPR defaults */ |
| /* ppe |
| * 1F - no full stop |
| * 08 - dynamic throttling starts if over 80 degrees |
| * 03 - dynamic throttling ceases if below 70 degrees */ |
| tpr.ppe = 0x1F0803; |
| /* spe |
| * 10 - full stopped when over 96 degrees |
| * 08 - dynamic throttling starts if over 80 degrees |
| * 03 - dynamic throttling ceases if below 70 degrees |
| */ |
| tpr.spe = 0x100803; |
| |
| /* STR defaults */ |
| /* str1 |
| * 10 - stop 16 of 32 cycles |
| */ |
| str1.val = 0x1010101010101010ull; |
| /* str2 |
| * 10 - stop 16 of 32 cycles |
| */ |
| str2 = 0x10; |
| |
| /* CR defaults */ |
| /* cr1 |
| * 4 - normal operation |
| */ |
| cr1.val = 0x0404040404040404ull; |
| /* cr2 |
| * 4 - normal operation |
| */ |
| cr2 = 0x04; |
| |
| for_each_possible_cpu (cpu) { |
| pr_debug("processing cpu %d\n", cpu); |
| sysdev = get_cpu_sysdev(cpu); |
| pmd_regs = cbe_get_cpu_pmd_regs(sysdev->id); |
| |
| out_be64(&pmd_regs->tm_str2, str2); |
| out_be64(&pmd_regs->tm_str1.val, str1.val); |
| out_be64(&pmd_regs->tm_tpr.val, tpr.val); |
| out_be64(&pmd_regs->tm_cr1.val, cr1.val); |
| out_be64(&pmd_regs->tm_cr2, cr2); |
| } |
| } |
| |
| |
| static int __init thermal_init(void) |
| { |
| init_default_values(); |
| |
| spu_add_sysdev_attr_group(&spu_attribute_group); |
| cpu_add_sysdev_attr_group(&ppe_attribute_group); |
| |
| return 0; |
| } |
| module_init(thermal_init); |
| |
| static void __exit thermal_exit(void) |
| { |
| spu_remove_sysdev_attr_group(&spu_attribute_group); |
| cpu_remove_sysdev_attr_group(&ppe_attribute_group); |
| } |
| module_exit(thermal_exit); |
| |
| MODULE_LICENSE("GPL"); |
| MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>"); |
| |