From patchwork Fri Apr 3 07:11:27 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hongtao Jia X-Patchwork-Id: 457851 X-Patchwork-Delegate: michael@ellerman.id.au Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [103.22.144.68]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id D350B1401E7 for ; Fri, 3 Apr 2015 18:15:56 +1100 (AEDT) Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 7A3EA1A0AE8 for ; Fri, 3 Apr 2015 18:15:56 +1100 (AEDT) X-Original-To: linuxppc-dev@lists.ozlabs.org Delivered-To: linuxppc-dev@lists.ozlabs.org Received: from na01-bl2-obe.outbound.protection.outlook.com (mail-bl2on0106.outbound.protection.outlook.com [65.55.169.106]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 7B7011A03D6 for ; Fri, 3 Apr 2015 18:14:57 +1100 (AEDT) Received: from BN3PR0301CA0077.namprd03.prod.outlook.com (25.160.152.173) by BN3PR03MB1478.namprd03.prod.outlook.com (25.163.35.141) with Microsoft SMTP Server (TLS) id 15.1.125.19; Fri, 3 Apr 2015 07:14:48 +0000 Received: from BY2FFO11FD002.protection.gbl (2a01:111:f400:7c0c::111) by BN3PR0301CA0077.outlook.office365.com (2a01:111:e400:401e::45) with Microsoft SMTP Server (TLS) id 15.1.130.23 via Frontend Transport; Fri, 3 Apr 2015 07:14:48 +0000 Authentication-Results: spf=fail (sender IP is 192.88.168.50) smtp.mailfrom=freescale.com; freescale.mail.onmicrosoft.com; dkim=none (message not signed) header.d=none; Received-SPF: Fail (protection.outlook.com: domain of freescale.com does not designate 192.88.168.50 as permitted sender) receiver=protection.outlook.com; client-ip=192.88.168.50; helo=tx30smr01.am.freescale.net; Received: from tx30smr01.am.freescale.net (192.88.168.50) by BY2FFO11FD002.mail.protection.outlook.com (10.1.14.124) with Microsoft SMTP Server (TLS) id 15.1.136.16 via Frontend Transport; Fri, 3 Apr 2015 07:14:48 +0000 Received: from titan.ap.freescale.net ([10.192.208.233]) by tx30smr01.am.freescale.net (8.14.3/8.14.0) with ESMTP id t337EhGF004702; Fri, 3 Apr 2015 00:14:45 -0700 From: Jia Hongtao To: Subject: [PATCH] QorIQ/TMU: add thermal management support based on TMU Date: Fri, 3 Apr 2015 15:11:27 +0800 Message-ID: <1428045087-15557-1-git-send-email-hongtao.jia@freescale.com> X-Mailer: git-send-email 2.1.0.27.g96db324 X-EOPAttributedMessage: 0 X-Forefront-Antispam-Report: CIP:192.88.168.50; CTRY:US; IPV:NLI; EFV:NLI; BMV:1; SFV:NSPM; SFS:(10019020)(6009001)(339900001)(22564002)(199003)(189002)(110136001)(19580395003)(77156002)(62966003)(104016003)(85426001)(575784001)(2351001)(6806004)(86362001)(50226001)(46102003)(19580405001)(33646002)(105606002)(106466001)(87936001)(48376002)(50986999)(77096005)(36756003)(92566002)(50466002)(47776003)(229853001)(473944003); DIR:OUT; SFP:1102; SCL:1; SRVR:BN3PR03MB1478; H:tx30smr01.am.freescale.net; FPR:; SPF:Fail; MLV:sfv; A:1; MX:1; LANG:en; MIME-Version: 1.0 X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:BN3PR03MB1478; X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(601004)(5002010)(5005006); SRVR:BN3PR03MB1478; BCL:0; PCL:0; RULEID:; SRVR:BN3PR03MB1478; X-Forefront-PRVS: 05352A48BE X-OriginatorOrg: freescale.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 03 Apr 2015 07:14:48.3488 (UTC) X-MS-Exchange-CrossTenant-Id: 710a03f5-10f6-4d38-9ff4-a80b81da590d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=710a03f5-10f6-4d38-9ff4-a80b81da590d; Ip=[192.88.168.50]; Helo=[tx30smr01.am.freescale.net] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: BN3PR03MB1478 Cc: scottwood@freescale.com, hongtao.jia@freescale.com, linuxppc-dev@lists.ozlabs.org, linux-pm@vger.kernel.org X-BeenThere: linuxppc-dev@lists.ozlabs.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Linux on PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@lists.ozlabs.org Sender: "Linuxppc-dev" It supports one critical trip point and one passive trip point. The cpufreq is used as the cooling device to throttle CPUs when the passive trip is crossed. Signed-off-by: Jia Hongtao --- drivers/thermal/Kconfig | 11 ++ drivers/thermal/Makefile | 1 + drivers/thermal/qoriq_thermal.c | 405 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 417 insertions(+) create mode 100644 drivers/thermal/qoriq_thermal.c diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index af40db0..c0a8bd1 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -147,6 +147,17 @@ config IMX_THERMAL cpufreq is used as the cooling device to throttle CPUs when the passive trip is crossed. +config QORIQ_THERMAL + tristate "Freescale QorIQ Thermal Monitoring Unit" + depends on CPU_THERMAL + depends on OF + default n + help + Enable thermal management based on Freescale QorIQ Thermal Monitoring + Unit (TMU). It supports one critical trip point and one passive trip + point. The cpufreq is used as the cooling device to throttle CPUs when + the passive trip is crossed. + config SPEAR_THERMAL bool "SPEAr thermal sensor driver" depends on PLAT_SPEAR diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index fa0dc48..7de4847 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o +obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c new file mode 100644 index 0000000..f5d3a2c --- /dev/null +++ b/drivers/thermal/qoriq_thermal.c @@ -0,0 +1,405 @@ +/* + * Copyright 2015 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + */ + +/* + * Based on Freescale QorIQ Thermal Monitoring Unit (TMU) + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define SITES_MAX 16 + +#define TMU_TEMP_PASSIVE 85000 +#define TMU_TEMP_CRITICAL 95000 + +#define TMU_PASSIVE_DELAY 1000 /* Milliseconds */ +#define TMU_POLLING_DELAY 5000 + +/* The driver supports 1 passive trip point and 1 critical trip point */ +enum tmu_thermal_trip { + TMU_TRIP_PASSIVE, + TMU_TRIP_CRITICAL, + TMU_TRIP_NUM, +}; + +/* + * QorIQ TMU Registers + */ +struct qoriq_tmu_site_regs { + __be32 tritsr; /* Immediate Temperature Site Register */ + __be32 tratsr; /* Average Temperature Site Register */ + u8 res0[0x8]; +} __packed; + +struct qoriq_tmu_regs { + __be32 tmr; /* Mode Register */ +#define TMR_DISABLE 0x0 +#define TMR_ME 0x80000000 +#define TMR_ALPF 0x0c000000 +#define TMR_MSITE 0x00008000 +#define TMR_ALL (TMR_ME | TMR_ALPF | TMR_MSITE) + __be32 tsr; /* Status Register */ + __be32 tmtmir; /* Temperature measurement interval Register */ +#define TMTMIR_DEFAULT 0x00000007 + u8 res0[0x14]; + __be32 tier; /* Interrupt Enable Register */ +#define TIER_DISABLE 0x0 + __be32 tidr; /* Interrupt Detect Register */ + __be32 tiscr; /* Interrupt Site Capture Register */ + __be32 ticscr; /* Interrupt Critical Site Capture Register */ + u8 res1[0x10]; + __be32 tmhtcrh; /* High Temperature Capture Register */ + __be32 tmhtcrl; /* Low Temperature Capture Register */ + u8 res2[0x8]; + __be32 tmhtitr; /* High Temperature Immediate Threshold */ + __be32 tmhtatr; /* High Temperature Average Threshold */ + __be32 tmhtactr; /* High Temperature Average Crit Threshold */ + u8 res3[0x24]; + __be32 ttcfgr; /* Temperature Configuration Register */ + __be32 tscfgr; /* Sensor Configuration Register */ + u8 res4[0x78]; + struct qoriq_tmu_site_regs site[SITES_MAX]; + u8 res5[0x9f8]; + __be32 ipbrr0; /* IP Block Revision Register 0 */ + __be32 ipbrr1; /* IP Block Revision Register 1 */ + u8 res6[0x310]; + __be32 ttr0cr; /* Temperature Range 0 Control Register */ + __be32 ttr1cr; /* Temperature Range 1 Control Register */ + __be32 ttr2cr; /* Temperature Range 2 Control Register */ + __be32 ttr3cr; /* Temperature Range 3 Control Register */ +}; + +/* + * Thermal zone data + */ +struct qoriq_tmu_data { + struct thermal_zone_device *tz; + struct thermal_cooling_device *cdev; + enum thermal_device_mode mode; + unsigned long temp_passive; + unsigned long temp_critical; + struct qoriq_tmu_regs __iomem *regs; +}; + +static int tmu_get_mode(struct thermal_zone_device *tz, + enum thermal_device_mode *mode) +{ + struct qoriq_tmu_data *data = tz->devdata; + + *mode = data->mode; + + return 0; +} + +static int tmu_set_mode(struct thermal_zone_device *tz, + enum thermal_device_mode mode) +{ + struct qoriq_tmu_data *data = tz->devdata; + + if (mode == THERMAL_DEVICE_ENABLED) { + tz->polling_delay = TMU_POLLING_DELAY; + tz->passive_delay = TMU_PASSIVE_DELAY; + thermal_zone_device_update(tz); + } else { + tz->polling_delay = 0; + tz->passive_delay = 0; + } + + data->mode = mode; + + return 0; +} + +static int tmu_get_temp(struct thermal_zone_device *tz, unsigned long *temp) +{ + u8 val; + struct qoriq_tmu_data *data = tz->devdata; + + val = ioread32be(&data->regs->site[0].tritsr); + *temp = (unsigned long)val * 1000; + + return 0; +} + +static int tmu_get_trip_type(struct thermal_zone_device *tz, int trip, + enum thermal_trip_type *type) +{ + *type = (trip == TMU_TRIP_PASSIVE) ? THERMAL_TRIP_PASSIVE : + THERMAL_TRIP_CRITICAL; + return 0; +} + +static int tmu_get_trip_temp(struct thermal_zone_device *tz, int trip, + unsigned long *temp) +{ + struct qoriq_tmu_data *data = tz->devdata; + + *temp = (trip == TMU_TRIP_PASSIVE) ? data->temp_passive : + data->temp_critical; + return 0; +} + +static int tmu_get_crit_temp(struct thermal_zone_device *tz, + unsigned long *temp) +{ + struct qoriq_tmu_data *data = tz->devdata; + + *temp = data->temp_critical; + return 0; +} + +static int tmu_get_trend(struct thermal_zone_device *thermal, + int trip, enum thermal_trend *trend) +{ + int ret; + unsigned long trip_temp; + + ret = tmu_get_trip_temp(thermal, trip, &trip_temp); + if (ret < 0) + return ret; + + if (thermal->temperature >= trip_temp) + *trend = THERMAL_TREND_RAISE_FULL; + else + *trend = THERMAL_TREND_DROP_FULL; + + return 0; +} + +static int tmu_bind(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev) +{ + int ret; + + ret = thermal_zone_bind_cooling_device(tz, TMU_TRIP_PASSIVE, cdev, + THERMAL_NO_LIMIT, + THERMAL_NO_LIMIT); + if (ret) { + dev_err(&tz->device, + "Binding zone %s with cdev %s failed:%d\n", + tz->type, cdev->type, ret); + return ret; + } + + return 0; +} + +static int tmu_unbind(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev) +{ + int ret; + + ret = thermal_zone_unbind_cooling_device(tz, TMU_TRIP_PASSIVE, cdev); + if (ret) { + dev_err(&tz->device, + "Unbinding zone %s with cdev %s failed:%d\n", + tz->type, cdev->type, ret); + return ret; + } + + return 0; +} + +static void qoriq_tmu_calibration(struct platform_device *pdev) +{ + int i, val, len; + const __be32 *calibration; + struct device_node *node = pdev->dev.of_node; + struct qoriq_tmu_data *data = dev_get_drvdata(&pdev->dev); + + /* Disable monitoring before calibration */ + iowrite32be(TMR_DISABLE, &data->regs->tmr); + + calibration = of_get_property(node, "calibration", &len); + if (calibration == NULL) { + pr_err("TMU: Failed to get calibration data.\n"); + return; + } + + for (i = 0; i < len; i += 8, calibration += 2) { + val = (int)of_read_number(calibration, 1); + iowrite32be(val, &data->regs->ttcfgr); + val = (int)of_read_number(calibration + 1, 1); + iowrite32be(val, &data->regs->tscfgr); + } +} + +static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) +{ + /* Disable interrupt, using polling instead */ + iowrite32be(TIER_DISABLE, &data->regs->tier); + + /* Set update_interval */ + iowrite32be(TMTMIR_DEFAULT, &data->regs->tmtmir); + + /* Enable monitoring */ + iowrite32be(TMR_ALL, &data->regs->tmr); +} + +static struct thermal_zone_device_ops tmu_tz_ops = { + .bind = tmu_bind, + .unbind = tmu_unbind, + .get_temp = tmu_get_temp, + .get_trend = tmu_get_trend, + .get_mode = tmu_get_mode, + .set_mode = tmu_set_mode, + .get_trip_type = tmu_get_trip_type, + .get_trip_temp = tmu_get_trip_temp, + .get_crit_temp = tmu_get_crit_temp, +}; + +static int qoriq_tmu_probe(struct platform_device *pdev) +{ + int ret; + struct cpumask clip_cpus; + struct qoriq_tmu_data *data; + + if (!cpufreq_get_current_driver()) { + dev_dbg(&pdev->dev, "No cpufreq driver yet\n"); + return -EPROBE_DEFER; + } + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "Device OF-Node is NULL"); + return -EFAULT; + } + + data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + dev_set_drvdata(&pdev->dev, data); + data->regs = of_iomap(pdev->dev.of_node, 0); + + if (!data->regs) { + dev_err(&pdev->dev, "Failed to get memory region\n"); + ret = -ENODEV; + goto err_iomap; + } + + qoriq_tmu_calibration(pdev); /* TMU calibration */ + + qoriq_tmu_init_device(data); /* TMU initialization */ + + cpumask_setall(&clip_cpus); + data->cdev = cpufreq_cooling_register(&clip_cpus); + if (IS_ERR(data->cdev)) { + ret = PTR_ERR(data->cdev); + dev_err(&data->cdev->device, + "Failed to register cpufreq cooling device: %d\n", ret); + goto err_cooling; + } + + data->temp_passive = TMU_TEMP_PASSIVE; + data->temp_critical = TMU_TEMP_CRITICAL; + data->tz = thermal_zone_device_register("tmu_thermal_zone", + TMU_TRIP_NUM, + 0, data, + &tmu_tz_ops, NULL, + TMU_PASSIVE_DELAY, + TMU_POLLING_DELAY); + + if (IS_ERR(data->tz)) { + ret = PTR_ERR(data->tz); + dev_err(&pdev->dev, + "Failed to register thermal zone device %d\n", ret); + goto err_thermal; + } + + data->mode = THERMAL_DEVICE_ENABLED; + + return 0; + +err_thermal: + cpufreq_cooling_unregister(data->cdev); + +err_cooling: + iounmap(data->regs); + +err_iomap: + dev_set_drvdata(&pdev->dev, NULL); + devm_kfree(&pdev->dev, data); + + return ret; +} + +static int qoriq_tmu_remove(struct platform_device *pdev) +{ + struct qoriq_tmu_data *data = dev_get_drvdata(&pdev->dev); + + /* Disable monitoring */ + iowrite32be(TMR_DISABLE, &data->regs->tmr); + + thermal_zone_device_unregister(data->tz); + cpufreq_cooling_unregister(data->cdev); + iounmap(data->regs); + + dev_set_drvdata(&pdev->dev, NULL); + devm_kfree(&pdev->dev, data); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int qoriq_tmu_suspend(struct device *dev) +{ + struct qoriq_tmu_data *data = dev_get_drvdata(dev); + + /* Disable monitoring */ + iowrite32be(TMR_DISABLE, &data->regs->tmr); + + return 0; +} + +static int qoriq_tmu_resume(struct device *dev) +{ + struct qoriq_tmu_data *data = dev_get_drvdata(dev); + + /* Enable monitoring */ + iowrite32be(TMR_ALL, &data->regs->tmr); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops, + qoriq_tmu_suspend, qoriq_tmu_resume); + +static const struct of_device_id qoriq_tmu_match[] = { + { .compatible = "fsl,qoriq-tmu", }, + {}, +}; + +static struct platform_driver qoriq_tmu = { + .driver = { + .owner = THIS_MODULE, + .name = "qoriq_thermal", + .pm = &qoriq_tmu_pm_ops, + .of_match_table = qoriq_tmu_match, + }, + .probe = qoriq_tmu_probe, + .remove = qoriq_tmu_remove, +}; +module_platform_driver(qoriq_tmu); + +MODULE_AUTHOR("Jia Hongtao "); +MODULE_DESCRIPTION("Freescale QorIQ Thermal Monitoring Unit driver"); +MODULE_LICENSE("GPL v2");