From patchwork Mon Feb 18 11:30:29 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wei Ni X-Patchwork-Id: 221256 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 833EC2C007A for ; Mon, 18 Feb 2013 22:31:32 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752468Ab3BRLbb (ORCPT ); Mon, 18 Feb 2013 06:31:31 -0500 Received: from hqemgate04.nvidia.com ([216.228.121.35]:10689 "EHLO hqemgate04.nvidia.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752193Ab3BRLb2 (ORCPT ); Mon, 18 Feb 2013 06:31:28 -0500 Received: from hqnvupgp07.nvidia.com (Not Verified[216.228.121.13]) by hqemgate04.nvidia.com id ; Mon, 18 Feb 2013 03:31:08 -0800 Received: from hqemhub01.nvidia.com ([172.20.150.30]) by hqnvupgp07.nvidia.com (PGP Universal service); Mon, 18 Feb 2013 03:30:38 -0800 X-PGP-Universal: processed; by hqnvupgp07.nvidia.com on Mon, 18 Feb 2013 03:30:38 -0800 Received: from hkemhub02.nvidia.com (10.18.67.13) by hqemhub01.nvidia.com (172.20.150.30) with Microsoft SMTP Server (TLS) id 8.3.297.1; Mon, 18 Feb 2013 03:31:16 -0800 Received: from niwei-ubuntu.nvidia.com (10.18.67.5) by hkemhub02.nvidia.com (10.18.67.13) with Microsoft SMTP Server id 8.3.297.1; Mon, 18 Feb 2013 19:31:11 +0800 From: Wei Ni To: , , , CC: , , , , , , Wei Ni Subject: [RFC PATCH 7/9] thermal: tegra30: add tegra30 thermal driver Date: Mon, 18 Feb 2013 19:30:29 +0800 Message-ID: <1361187031-3679-8-git-send-email-wni@nvidia.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1361187031-3679-1-git-send-email-wni@nvidia.com> References: <1361187031-3679-1-git-send-email-wni@nvidia.com> X-NVConfidentiality: public MIME-Version: 1.0 Sender: linux-tegra-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-tegra@vger.kernel.org dd Tegra30 thermal driver support. It create thermal zone with thermal sensors and cooling device to participate in the linux thermal management. Signed-off-by: Wei Ni --- drivers/thermal/Kconfig | 9 ++ drivers/thermal/Makefile | 1 + drivers/thermal/tegra3_thermal.c | 289 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 299 insertions(+) create mode 100644 drivers/thermal/tegra3_thermal.c diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index eadef5b..2403681 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -141,6 +141,15 @@ config INTEL_POWERCLAMP enforce idle time which results in more package C-state residency. The user interface is exposed via generic thermal framework. +config TEGRA30_THERMAL + tristate "Tegra30 thermal driver" + depends on ARCH_TEGRA + help + Select this to enable the Tegra30 thermal driver. Adds Tegra30 thermal + implementation according to the thermal management framework. Create + thermal zone with thermal sensors and cooling device to participate + in the linux thermal management. + config THERMAL_TEST tristate "test driver" help diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index ee0f687..de0b411 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o +obj-$(CONFIG_TEGRA30_THERMAL) += tegra3_thermal.o # dummy driver for testing obj-$(CONFIG_THERMAL_TEST) += thermal_test.o diff --git a/drivers/thermal/tegra3_thermal.c b/drivers/thermal/tegra3_thermal.c new file mode 100644 index 0000000..384168f --- /dev/null +++ b/drivers/thermal/tegra3_thermal.c @@ -0,0 +1,289 @@ +/* + * Tegra thermal driver. + * + * Copyright (C) 2010-2013 NVIDIA Corporation. All rights reserved. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_THROT_TABLE_SIZE (64) + +struct throttle_table { + unsigned int cpu_freq; + int core_cap_level; +}; + +struct balanced_throttle { + struct thermal_cooling_device *cdev; + struct list_head node; + int is_throttling; + int throttle_count; + int throttle_index; + int throt_tab_size; + struct throttle_table throt_tab[MAX_THROT_TABLE_SIZE + 1]; +}; + +struct tegra_thermal_data { + struct thermal_zone *tz; + struct node_args np_args; + int passive_delay; + struct balanced_throttle tj_throttle; + struct thermal_trip_point trip_ext; +}; + +static DEFINE_MUTEX(cpu_throttle_lock); + +static int tegra_throttle_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *max_state) +{ + struct balanced_throttle *bthrot = cdev->devdata; + + *max_state = bthrot->throt_tab_size; + + return 0; +} + +static int +tegra_throttle_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *cur_state) +{ + struct balanced_throttle *bthrot = cdev->devdata; + + mutex_lock(&cpu_throttle_lock); + *cur_state = bthrot->is_throttling ? + (bthrot->throt_tab_size - bthrot->throttle_index) : + 0; + mutex_unlock(&cpu_throttle_lock); + + return 0; +} + +static int +tegra_throttle_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long cur_state) +{ + struct balanced_throttle *bthrot = cdev->devdata; + int index; + + mutex_lock(&cpu_throttle_lock); + + /* TODO: we will handle the dvfs here */ + + bthrot->throttle_index = bthrot->throt_tab_size - cur_state; + index = bthrot->throttle_index; + + mutex_unlock(&cpu_throttle_lock); + + return 0; +} + +static struct thermal_cooling_device_ops tegra_throttle_cooling_ops = { + .get_max_state = tegra_throttle_get_max_state, + .get_cur_state = tegra_throttle_get_cur_state, + .set_cur_state = tegra_throttle_set_cur_state, +}; + +struct thermal_cooling_device *balanced_throttle_register( + struct balanced_throttle *bthrot, char *type) +{ + bthrot->cdev = thermal_cooling_device_register(type, bthrot, + &tegra_throttle_cooling_ops); + + if (IS_ERR(bthrot->cdev)) { + bthrot->cdev = NULL; + return ERR_PTR(-ENODEV); + } + + return bthrot->cdev; +} + +static struct tegra_thermal_data * __devinit thermal_tegra_dt_parse_pdata( + struct platform_device *pdev) +{ + struct tegra_thermal_data *tdata; + struct device_node *np = pdev->dev.of_node; + struct of_phandle_args args; + u32 val; + int ret; + + if (!np) + return NULL; + + tdata = devm_kzalloc(&pdev->dev, sizeof(*tdata), GFP_KERNEL); + if (!tdata) { + dev_err(&pdev->dev, "Can't allocate platform data\n"); + return NULL; + } + memset(tdata, 0, sizeof(*tdata)); + + ret = of_parse_phandle_with_args(np, "sensors", "#sensor-cells", 0, + &args); + if (ret) { + dev_err(&pdev->dev, "Can't get sensor.\n"); + return NULL; + } + tdata->np_args.np = args.np; + tdata->np_args.index = args.args[0]; + + ret = of_property_read_u32(np, "passive-delay", &val); + if (!ret) + tdata->passive_delay = val; + + ret = of_property_read_u32(np, "num-passive-trips", &val); + if (!ret) + tdata->trip_ext.num_passive_trips = val; + + if (tdata->trip_ext.num_passive_trips) { + tdata->trip_ext.passive_trips = devm_kzalloc(&pdev->dev, + sizeof(int) * val, GFP_KERNEL); + + of_property_read_u32_array(np, "passive-trips", + (u32 *)(tdata->trip_ext.passive_trips), + tdata->trip_ext.num_passive_trips); + } + + ret = of_property_read_u32(np, "num-active-trips", &val); + if (!ret) + tdata->trip_ext.num_active_trips = val; + + if (tdata->trip_ext.num_active_trips) { + tdata->trip_ext.active_trips = devm_kzalloc(&pdev->dev, + sizeof(int) * val, GFP_KERNEL); + + of_property_read_u32_array(np, "active-trips", + (u32 *)(tdata->trip_ext.active_trips), + tdata->trip_ext.num_active_trips); + } + + ret = of_property_read_u32(np, "throt-tab-size", &val); + if (!ret) + tdata->tj_throttle.throt_tab_size = val; + + of_property_read_u32_array(np, "throt-tab", + (u32 *)(&tdata->tj_throttle.throt_tab), + tdata->tj_throttle.throt_tab_size * 2); + + return tdata; +} + +static int tegra30_thermal_probe(struct platform_device *pdev) +{ + struct tegra_thermal_data *pdata = pdev->dev.platform_data; + struct thermal_zone *tz; + struct thermal_sensor *ts; + static struct thermal_cooling_device *cdev; + int ret; + + pdata = thermal_tegra_dt_parse_pdata(pdev); + if (!pdata) { + dev_err(&pdev->dev, "Get platform data failed.\n"); + return -EINVAL; + } + + /* Create a thermal zone */ + tz = create_thermal_zone("tz_tegra", NULL); + if (!tz) { + dev_err(&pdev->dev, "Create thermal_zone failed.\n"); + return -EINVAL; + } + + pdata->tz = tz; + + /* Register cooling device */ + cdev = balanced_throttle_register(&pdata->tj_throttle, "cdev_throttle"); + if (!cdev) { + dev_err(&pdev->dev, "Register cooling device failed.\n"); + goto exit_remove_thermal_zone; + } + + /* Get sensor */ + ts = get_sensor_by_node(&pdata->np_args); + if (!ts) { + dev_err(&pdev->dev, "get_sensor_by_node failed.\n"); + goto exit_unregister_cooling; + } + + ret = add_sensor_to_zone(pdata->tz, ts); + if (ret) { + dev_err(&pdev->dev, "add_sensor_to_zone failed.\n"); + goto exit_unregister_cooling; + } + + ret = add_cdev_to_zone(pdata->tz, cdev); + if (ret) { + dev_err(&pdev->dev, "add_cdev_to_zone failed.\n"); + goto exit_unregister_cooling; + } + + ret = add_sensor_trip_info(pdata->tz, ts, &pdata->trip_ext); + if (ret) { + dev_err(&pdev->dev, "add_sensor_trip_info failed.\n"); + goto exit_unregister_cooling; + } + + return 0; + +exit_unregister_cooling: + thermal_cooling_device_unregister(cdev); +exit_remove_thermal_zone: + remove_thermal_zone(pdata->tz); + return -EINVAL; + +} + +static int tegra30_thermal_remove(struct platform_device *pdev) +{ + struct tegra_thermal_data *pdata = pdev->dev.platform_data; + int i; + + for (i = 0; i < MAX_CDEVS_PER_ZONE; i++) { + if (pdata->tz->cdevs[i]) + thermal_cooling_device_unregister(pdata->tz->cdevs[i]); + else + break; + } + + remove_thermal_zone(pdata->tz); + + return 0; +} + +static const struct of_device_id tegra30_thermal_id_table[] = { + { .compatible = "nvidia,tegra30-thermal" }, + {} +}; +MODULE_DEVICE_TABLE(of, tegra30_thermal_id_table); + +static struct platform_driver tegra3_thermal_driver = { + .probe = tegra30_thermal_probe, + .remove = tegra30_thermal_remove, + .driver = { + .name = "tegra_thermal", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tegra30_thermal_id_table), + }, +}; +module_platform_driver(tegra3_thermal_driver); + +MODULE_AUTHOR("Wei Ni "); +MODULE_DESCRIPTION("Tegra30 thermal throttle driver"); +MODULE_LICENSE("GPL v2");