brintos

brintos / linux-shallow public Read only

0
0
Text · 5.9 KiB · 576f88b Raw
244 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * db8500_thermal.c - DB8500 Thermal Management Implementation4 *5 * Copyright (C) 2012 ST-Ericsson6 * Copyright (C) 2012-2019 Linaro Ltd.7 *8 * Authors: Hongbo Zhang, Linus Walleij9 */10 11#include <linux/cpu_cooling.h>12#include <linux/interrupt.h>13#include <linux/mfd/dbx500-prcmu.h>14#include <linux/module.h>15#include <linux/of.h>16#include <linux/platform_device.h>17#include <linux/slab.h>18#include <linux/thermal.h>19 20#define PRCMU_DEFAULT_MEASURE_TIME	0xFFF21#define PRCMU_DEFAULT_LOW_TEMP		022 23/**24 * db8500_thermal_points - the interpolation points that trigger25 * interrupts26 */27static const unsigned long db8500_thermal_points[] = {28	15000,29	20000,30	25000,31	30000,32	35000,33	40000,34	45000,35	50000,36	55000,37	60000,38	65000,39	70000,40	75000,41	80000,42	/*43	 * This is where things start to get really bad for the44	 * SoC and the thermal zones should be set up to trigger45	 * critical temperature at 85000 mC so we don't get above46	 * this point.47	 */48	85000,49	90000,50	95000,51	100000,52};53 54struct db8500_thermal_zone {55	struct thermal_zone_device *tz;56	struct device *dev;57	unsigned long interpolated_temp;58	unsigned int cur_index;59};60 61/* Callback to get current temperature */62static int db8500_thermal_get_temp(struct thermal_zone_device *tz, int *temp)63{64	struct db8500_thermal_zone *th = thermal_zone_device_priv(tz);65 66	/*67	 * TODO: There is no PRCMU interface to get temperature data currently,68	 * so a pseudo temperature is returned , it works for thermal framework69	 * and this will be fixed when the PRCMU interface is available.70	 */71	*temp = th->interpolated_temp;72 73	return 0;74}75 76static const struct thermal_zone_device_ops thdev_ops = {77	.get_temp = db8500_thermal_get_temp,78};79 80static void db8500_thermal_update_config(struct db8500_thermal_zone *th,81					 unsigned int idx,82					 unsigned long next_low,83					 unsigned long next_high)84{85	prcmu_stop_temp_sense();86 87	th->cur_index = idx;88	th->interpolated_temp = (next_low + next_high)/2;89 90	/*91	 * The PRCMU accept absolute temperatures in celsius so divide92	 * down the millicelsius with 100093	 */94	prcmu_config_hotmon((u8)(next_low/1000), (u8)(next_high/1000));95	prcmu_start_temp_sense(PRCMU_DEFAULT_MEASURE_TIME);96}97 98static irqreturn_t prcmu_low_irq_handler(int irq, void *irq_data)99{100	struct db8500_thermal_zone *th = irq_data;101	unsigned int idx = th->cur_index;102	unsigned long next_low, next_high;103 104	if (idx == 0)105		/* Meaningless for thermal management, ignoring it */106		return IRQ_HANDLED;107 108	if (idx == 1) {109		next_high = db8500_thermal_points[0];110		next_low = PRCMU_DEFAULT_LOW_TEMP;111	} else {112		next_high = db8500_thermal_points[idx - 1];113		next_low = db8500_thermal_points[idx - 2];114	}115	idx -= 1;116 117	db8500_thermal_update_config(th, idx, next_low, next_high);118	dev_dbg(th->dev,119		"PRCMU set max %ld, min %ld\n", next_high, next_low);120 121	thermal_zone_device_update(th->tz, THERMAL_EVENT_UNSPECIFIED);122 123	return IRQ_HANDLED;124}125 126static irqreturn_t prcmu_high_irq_handler(int irq, void *irq_data)127{128	struct db8500_thermal_zone *th = irq_data;129	unsigned int idx = th->cur_index;130	unsigned long next_low, next_high;131	int num_points = ARRAY_SIZE(db8500_thermal_points);132 133	if (idx < num_points - 1) {134		next_high = db8500_thermal_points[idx+1];135		next_low = db8500_thermal_points[idx];136		idx += 1;137 138		db8500_thermal_update_config(th, idx, next_low, next_high);139 140		dev_dbg(th->dev,141			"PRCMU set max %ld, min %ld\n", next_high, next_low);142	} else if (idx == num_points - 1)143		/* So we roof out 1 degree over the max point */144		th->interpolated_temp = db8500_thermal_points[idx] + 1;145 146	thermal_zone_device_update(th->tz, THERMAL_EVENT_UNSPECIFIED);147 148	return IRQ_HANDLED;149}150 151static int db8500_thermal_probe(struct platform_device *pdev)152{153	struct db8500_thermal_zone *th = NULL;154	struct device *dev = &pdev->dev;155	int low_irq, high_irq, ret = 0;156 157	th = devm_kzalloc(dev, sizeof(*th), GFP_KERNEL);158	if (!th)159		return -ENOMEM;160 161	th->dev = dev;162 163	low_irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_LOW");164	if (low_irq < 0)165		return low_irq;166 167	ret = devm_request_threaded_irq(dev, low_irq, NULL,168		prcmu_low_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,169		"dbx500_temp_low", th);170	if (ret < 0) {171		dev_err(dev, "failed to allocate temp low irq\n");172		return ret;173	}174 175	high_irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_HIGH");176	if (high_irq < 0)177		return high_irq;178 179	ret = devm_request_threaded_irq(dev, high_irq, NULL,180		prcmu_high_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,181		"dbx500_temp_high", th);182	if (ret < 0) {183		dev_err(dev, "failed to allocate temp high irq\n");184		return ret;185	}186 187	/* register of thermal sensor and get info from DT */188	th->tz = devm_thermal_of_zone_register(dev, 0, th, &thdev_ops);189	if (IS_ERR(th->tz)) {190		dev_err(dev, "register thermal zone sensor failed\n");191		return PTR_ERR(th->tz);192	}193	dev_info(dev, "thermal zone sensor registered\n");194 195	/* Start measuring at the lowest point */196	db8500_thermal_update_config(th, 0, PRCMU_DEFAULT_LOW_TEMP,197				     db8500_thermal_points[0]);198 199	platform_set_drvdata(pdev, th);200 201	return 0;202}203 204static int db8500_thermal_suspend(struct platform_device *pdev,205		pm_message_t state)206{207	prcmu_stop_temp_sense();208 209	return 0;210}211 212static int db8500_thermal_resume(struct platform_device *pdev)213{214	struct db8500_thermal_zone *th = platform_get_drvdata(pdev);215 216	/* Resume and start measuring at the lowest point */217	db8500_thermal_update_config(th, 0, PRCMU_DEFAULT_LOW_TEMP,218				     db8500_thermal_points[0]);219 220	return 0;221}222 223static const struct of_device_id db8500_thermal_match[] = {224	{ .compatible = "stericsson,db8500-thermal" },225	{},226};227MODULE_DEVICE_TABLE(of, db8500_thermal_match);228 229static struct platform_driver db8500_thermal_driver = {230	.driver = {231		.name = "db8500-thermal",232		.of_match_table = db8500_thermal_match,233	},234	.probe = db8500_thermal_probe,235	.suspend = db8500_thermal_suspend,236	.resume = db8500_thermal_resume,237};238 239module_platform_driver(db8500_thermal_driver);240 241MODULE_AUTHOR("Hongbo Zhang <hongbo.zhang@stericsson.com>");242MODULE_DESCRIPTION("DB8500 thermal driver");243MODULE_LICENSE("GPL");244