From patchwork Tue Apr 21 07:44:53 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yang Shen X-Patchwork-Id: 212252 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 80FC5C55183 for ; Tue, 21 Apr 2020 07:46:00 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 649EE2087E for ; Tue, 21 Apr 2020 07:46:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726052AbgDUHp7 (ORCPT ); Tue, 21 Apr 2020 03:45:59 -0400 Received: from szxga05-in.huawei.com ([45.249.212.191]:2817 "EHLO huawei.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1725992AbgDUHp7 (ORCPT ); Tue, 21 Apr 2020 03:45:59 -0400 Received: from DGGEMS406-HUB.china.huawei.com (unknown [172.30.72.59]) by Forcepoint Email with ESMTP id DF47D1B54884F628EAD4; Tue, 21 Apr 2020 15:45:55 +0800 (CST) Received: from localhost.localdomain (10.69.192.56) by DGGEMS406-HUB.china.huawei.com (10.3.19.206) with Microsoft SMTP Server id 14.3.487.0; Tue, 21 Apr 2020 15:45:47 +0800 From: Yang Shen To: , , CC: , , Subject: [PATCH V3 2/2] thermal: Add HiSilicon Kunpeng thermal driver Date: Tue, 21 Apr 2020 15:44:53 +0800 Message-ID: <1587455093-33876-3-git-send-email-shenyang39@huawei.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1587455093-33876-1-git-send-email-shenyang39@huawei.com> References: <1587455093-33876-1-git-send-email-shenyang39@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.69.192.56] X-CFilter-Loop: Reflected Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org Support HiSilicon Kunpeng tsensor. the driver will report the max temperature for each core. Signed-off-by: Yang Shen Signed-off-by: Zhou Wang Signed-off-by: Kunshan Tang --- drivers/thermal/Kconfig | 8 ++ drivers/thermal/Makefile | 1 + drivers/thermal/kunpeng_thermal.c | 216 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 225 insertions(+) create mode 100644 drivers/thermal/kunpeng_thermal.c -- 2.7.4 diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 5a05db5..7611b5d 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -239,6 +239,14 @@ config HISI_THERMAL thermal framework. cpufreq is used as the cooling device to throttle CPUs when the passive trip is crossed. +config KUNPENG_THERMAL + tristate "HiSilicon kunpeng thermal driver" + depends on ARM64 || COMPILE_TEST + help + Enable this to plug HiSilicon kunpeng's thermal sensors driver into + the Linux thermal framework, which supports to get the highest + temperature of one Kunpeng SoC. + config IMX_THERMAL tristate "Temperature sensor driver for Freescale i.MX SoCs" depends on ARCH_MXC || COMPILE_TEST diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 9fb88e2..88ffca5 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -57,3 +57,4 @@ obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o obj-$(CONFIG_ZX2967_THERMAL) += zx2967_thermal.o obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o obj-$(CONFIG_AMLOGIC_THERMAL) += amlogic_thermal.o +obj-$(CONFIG_KUNPENG_THERMAL) += kunpeng_thermal.o diff --git a/drivers/thermal/kunpeng_thermal.c b/drivers/thermal/kunpeng_thermal.c new file mode 100644 index 0000000..d22e875 --- /dev/null +++ b/drivers/thermal/kunpeng_thermal.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2020 HiSilicon Limited. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define KUNPENG_TSENSOR_CONTROL 0x20D0 +#define KUNPENG_TSENSOR_ST 0x60D0 +#define KUNPENG_TSENSOR_SAMPLE 0x60D4 +#define KUNPENG_TSENSOR_CONTROL_EN BIT(0) + +#define KUNPENG_TSENSOR_IS_READY(reg) (((reg) >> 12) & 0x1) +#define KUNPENG_TSENSOR_IS_VALID(reg) (((reg) >> 31) & 0x1) +#define KUNPENG_TSENSOR_TRIM_HIGH(reg) (((reg) >> 15) & 0x7FF) +#define KUNPENG_TSENSOR_TRIM_LOW(reg) ((reg) & 0x7FF) +#define KUNPENG_TSENSOR_TEMP_VAL(reg) ((reg) & 0x3FF) +#define KUNPENG_TSENSOR_BASE_NUM(num) (2 * (num)) +#define KUNPENG_TSENSOR_TRIM_NUM(num) (2 * (num) + 1) + +#define KUNPENG_TSENSOR_RD_INTVRL_US 5 +#define KUNPENG_TSENSOR_RD_TMOUT_US 5000 +#define KUNPENG_TSENSOR_BASIC_TMP 25000 +#define KUNPENG_TSENSOR_BASIC_TRIM_RANGE 80000 + +struct kunpeng_tsensor { + void __iomem *base; + void __iomem *trim_register; +}; + +struct kunpeng_thermal_dev { + u32 num_tsensors; + struct kunpeng_tsensor tsensor[]; +}; + +static int kunpeng_thermal_temp_correct(u32 tmp, u32 trim) +{ + int trim_high = KUNPENG_TSENSOR_TRIM_HIGH(trim); + int trim_low = KUNPENG_TSENSOR_TRIM_LOW(trim); + int val = KUNPENG_TSENSOR_TEMP_VAL(tmp); + + if (trim_high == trim_low) + return INT_MIN; + + /* temperature of tsensor needs to be calibrated */ + return KUNPENG_TSENSOR_BASIC_TRIM_RANGE * (val - trim_low) / + (trim_high - trim_low) + KUNPENG_TSENSOR_BASIC_TMP; +} + +static int kunpeng_thermal_get_temp(struct thermal_zone_device *thermal, + int *temp) +{ + struct kunpeng_thermal_dev *tdev = thermal->devdata; + int tempmax = INT_MIN; + u32 i, reg, tmp, trim; + int ret; + + for (i = 0; i < tdev->num_tsensors; i++) { + /* Waiting for tsensor ready */ + ret = readl_relaxed_poll_timeout(tdev->tsensor[i].base + + KUNPENG_TSENSOR_ST, reg, + KUNPENG_TSENSOR_IS_READY(reg), + KUNPENG_TSENSOR_RD_INTVRL_US, + KUNPENG_TSENSOR_RD_TMOUT_US); + if (ret) { + dev_err(&thermal->device, + "Tsensor%u isn't ready!\n", i); + continue; + } + + /* checking if temperatures are valid */ + tmp = readl_relaxed(tdev->tsensor[i].base + + KUNPENG_TSENSOR_SAMPLE); + if (!KUNPENG_TSENSOR_IS_VALID(tmp)) { + dev_err(&thermal->device, + "Tsensor%u temperature is invalid!\n", i); + continue; + } + + trim = readl_relaxed(tdev->tsensor[i].trim_register); + + ret = kunpeng_thermal_temp_correct(tmp, trim); + if (ret == INT_MIN) { + dev_err(&thermal->device, + "Tsensor%u trim value is invalid!\n", i); + continue; + } + + tempmax = max(ret, tempmax); + } + + if (tempmax == INT_MIN) + return -EINVAL; + + *temp = tempmax; + + return 0; +} + +static struct thermal_zone_device_ops ops = { + .get_temp = kunpeng_thermal_get_temp, +}; + +static int kunpeng_thermal_get_iobase(struct platform_device *pdev, + struct kunpeng_tsensor *tsensor, + u32 resource_num) +{ + struct resource *res; + void __iomem *base; + + res = platform_get_resource(pdev, IORESOURCE_MEM, resource_num); + if (!res) + return -EINVAL; + + base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (IS_ERR(base)) + return -EINVAL; + + if (resource_num & 1) + tsensor->trim_register = base; + else + tsensor->base = base; + + return 0; +} + +static int kunpeng_thermal_probe(struct platform_device *pdev) +{ + u32 num_tsensors = pdev->num_resources >> 1; + struct thermal_zone_device *thermal_zone; + struct kunpeng_thermal_dev *tdev; + u32 i, reg; + int ret; + + tdev = devm_kzalloc(&pdev->dev, sizeof(*tdev) + sizeof(*tdev->tsensor) * + num_tsensors, GFP_KERNEL); + if (!tdev) + return -ENOMEM; + + tdev->num_tsensors = num_tsensors; + + for (i = 0; i < num_tsensors; i++) { + ret = kunpeng_thermal_get_iobase(pdev, &tdev->tsensor[i], + KUNPENG_TSENSOR_BASE_NUM(i)); + if (ret) { + dev_err(&pdev->dev, "Fail to ioremap base!\n"); + return ret; + } + + ret = kunpeng_thermal_get_iobase(pdev, &tdev->tsensor[i], + KUNPENG_TSENSOR_TRIM_NUM(i)); + if (ret) { + dev_err(&pdev->dev, "Fail to ioremap trim register!\n"); + return ret; + } + + reg = readl_relaxed(tdev->tsensor[i].base + + KUNPENG_TSENSOR_CONTROL); + writel_relaxed(reg | KUNPENG_TSENSOR_CONTROL_EN, + tdev->tsensor[i].base + + KUNPENG_TSENSOR_CONTROL); + } + + thermal_zone = thermal_zone_device_register("kunpeng_thermal", 0, 0, + tdev, &ops, NULL, 0, 0); + if (IS_ERR(thermal_zone)) { + dev_err(&pdev->dev, "Fail to register to thermal subsystem\n"); + return PTR_ERR(thermal_zone); + } + + platform_set_drvdata(pdev, thermal_zone); + + return 0; +} + +/** + * kunpeng_thermal_remove() - Unregister device from thermal. + * + * This driver and IMU share tsensor devices. This function only unregister + * devices from thermal but never disable tsensors. + */ +static int kunpeng_thermal_remove(struct platform_device *pdev) +{ + struct thermal_zone_device *thermal_zone = platform_get_drvdata(pdev); + + thermal_zone_device_unregister(thermal_zone); + + return 0; +} + +static const struct acpi_device_id kunpeng_thermal_acpi_match[] = { + { "HISI0371", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, kunpeng_thermal_acpi_match); + +static struct platform_driver kunpeng_thermal_driver = { + .probe = kunpeng_thermal_probe, + .remove = kunpeng_thermal_remove, + .driver = { + .name = "kunpeng_thermal", + .acpi_match_table = ACPI_PTR(kunpeng_thermal_acpi_match), + }, +}; + +module_platform_driver(kunpeng_thermal_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Yang Shen "); +MODULE_DESCRIPTION("HiSilicon Kunpeng thermal driver");