Linux Audio

Check our new training course

Loading...
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Junction temperature thermal driver for Maxim Max77620.
  4 *
  5 * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
  6 *
  7 * Author: Laxman Dewangan <ldewangan@nvidia.com>
  8 *	   Mallikarjun Kasoju <mkasoju@nvidia.com>
  9 */
 10
 11#include <linux/irq.h>
 12#include <linux/interrupt.h>
 13#include <linux/mfd/max77620.h>
 14#include <linux/module.h>
 15#include <linux/of.h>
 16#include <linux/platform_device.h>
 17#include <linux/regmap.h>
 18#include <linux/slab.h>
 19#include <linux/thermal.h>
 20
 21#define MAX77620_NORMAL_OPERATING_TEMP	100000
 22#define MAX77620_TJALARM1_TEMP		120000
 23#define MAX77620_TJALARM2_TEMP		140000
 24
 25struct max77620_therm_info {
 26	struct device			*dev;
 27	struct regmap			*rmap;
 28	struct thermal_zone_device	*tz_device;
 29	int				irq_tjalarm1;
 30	int				irq_tjalarm2;
 31};
 32
 33/**
 34 * max77620_thermal_read_temp: Read PMIC die temperatue.
 35 * @data:	Device specific data.
 36 * @temp:	Temperature in millidegrees Celsius
 37 *
 38 * The actual temperature of PMIC die is not available from PMIC.
 39 * PMIC only tells the status if it has crossed or not the threshold level
 40 * of 120degC or 140degC.
 41 * If threshold has not been crossed then assume die temperature as 100degC
 42 * else 120degC or 140deG based on the PMIC die temp threshold status.
 43 *
 44 * Return 0 on success otherwise error number to show reason of failure.
 45 */
 46
 47static int max77620_thermal_read_temp(struct thermal_zone_device *tz, int *temp)
 48{
 49	struct max77620_therm_info *mtherm = thermal_zone_device_priv(tz);
 50	unsigned int val;
 51	int ret;
 52
 53	ret = regmap_read(mtherm->rmap, MAX77620_REG_STATLBT, &val);
 54	if (ret < 0)
 
 55		return ret;
 
 56
 57	if (val & MAX77620_IRQ_TJALRM2_MASK)
 58		*temp = MAX77620_TJALARM2_TEMP;
 59	else if (val & MAX77620_IRQ_TJALRM1_MASK)
 60		*temp = MAX77620_TJALARM1_TEMP;
 61	else
 62		*temp = MAX77620_NORMAL_OPERATING_TEMP;
 63
 64	return 0;
 65}
 66
 67static const struct thermal_zone_device_ops max77620_thermal_ops = {
 68	.get_temp = max77620_thermal_read_temp,
 69};
 70
 71static irqreturn_t max77620_thermal_irq(int irq, void *data)
 72{
 73	struct max77620_therm_info *mtherm = data;
 74
 75	if (irq == mtherm->irq_tjalarm1)
 76		dev_warn(mtherm->dev, "Junction Temp Alarm1(120C) occurred\n");
 77	else if (irq == mtherm->irq_tjalarm2)
 78		dev_crit(mtherm->dev, "Junction Temp Alarm2(140C) occurred\n");
 79
 80	thermal_zone_device_update(mtherm->tz_device,
 81				   THERMAL_EVENT_UNSPECIFIED);
 82
 83	return IRQ_HANDLED;
 84}
 85
 86static int max77620_thermal_probe(struct platform_device *pdev)
 87{
 88	struct max77620_therm_info *mtherm;
 89	int ret;
 90
 91	mtherm = devm_kzalloc(&pdev->dev, sizeof(*mtherm), GFP_KERNEL);
 92	if (!mtherm)
 93		return -ENOMEM;
 94
 95	mtherm->irq_tjalarm1 = platform_get_irq(pdev, 0);
 96	mtherm->irq_tjalarm2 = platform_get_irq(pdev, 1);
 97	if ((mtherm->irq_tjalarm1 < 0) || (mtherm->irq_tjalarm2 < 0)) {
 98		dev_err(&pdev->dev, "Alarm irq number not available\n");
 99		return -EINVAL;
100	}
101
102	mtherm->dev = &pdev->dev;
103	mtherm->rmap = dev_get_regmap(pdev->dev.parent, NULL);
104	if (!mtherm->rmap) {
105		dev_err(&pdev->dev, "Failed to get parent regmap\n");
106		return -ENODEV;
107	}
108
109	/*
110	 * The reference taken to the parent's node which will be balanced on
111	 * reprobe or on platform-device release.
112	 */
113	device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent);
114
115	mtherm->tz_device = devm_thermal_of_zone_register(&pdev->dev, 0,
116				mtherm, &max77620_thermal_ops);
117	if (IS_ERR(mtherm->tz_device))
118		return PTR_ERR(mtherm->tz_device);
 
 
 
 
119
120	ret = devm_request_threaded_irq(&pdev->dev, mtherm->irq_tjalarm1, NULL,
121					max77620_thermal_irq,
122					IRQF_ONESHOT | IRQF_SHARED,
123					dev_name(&pdev->dev), mtherm);
124	if (ret < 0) {
125		dev_err(&pdev->dev, "Failed to request irq1: %d\n", ret);
126		return ret;
127	}
128
129	ret = devm_request_threaded_irq(&pdev->dev, mtherm->irq_tjalarm2, NULL,
130					max77620_thermal_irq,
131					IRQF_ONESHOT | IRQF_SHARED,
132					dev_name(&pdev->dev), mtherm);
133	if (ret < 0) {
134		dev_err(&pdev->dev, "Failed to request irq2: %d\n", ret);
135		return ret;
136	}
 
 
137
138	return 0;
139}
140
141static struct platform_device_id max77620_thermal_devtype[] = {
142	{ .name = "max77620-thermal", },
143	{},
144};
145MODULE_DEVICE_TABLE(platform, max77620_thermal_devtype);
146
147static struct platform_driver max77620_thermal_driver = {
148	.driver = {
149		.name = "max77620-thermal",
150	},
151	.probe = max77620_thermal_probe,
152	.id_table = max77620_thermal_devtype,
153};
154
155module_platform_driver(max77620_thermal_driver);
156
157MODULE_DESCRIPTION("Max77620 Junction temperature Thermal driver");
158MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
159MODULE_AUTHOR("Mallikarjun Kasoju <mkasoju@nvidia.com>");
160MODULE_LICENSE("GPL v2");
v5.4
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Junction temperature thermal driver for Maxim Max77620.
  4 *
  5 * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
  6 *
  7 * Author: Laxman Dewangan <ldewangan@nvidia.com>
  8 *	   Mallikarjun Kasoju <mkasoju@nvidia.com>
  9 */
 10
 11#include <linux/irq.h>
 12#include <linux/interrupt.h>
 13#include <linux/mfd/max77620.h>
 14#include <linux/module.h>
 15#include <linux/of.h>
 16#include <linux/platform_device.h>
 17#include <linux/regmap.h>
 18#include <linux/slab.h>
 19#include <linux/thermal.h>
 20
 21#define MAX77620_NORMAL_OPERATING_TEMP	100000
 22#define MAX77620_TJALARM1_TEMP		120000
 23#define MAX77620_TJALARM2_TEMP		140000
 24
 25struct max77620_therm_info {
 26	struct device			*dev;
 27	struct regmap			*rmap;
 28	struct thermal_zone_device	*tz_device;
 29	int				irq_tjalarm1;
 30	int				irq_tjalarm2;
 31};
 32
 33/**
 34 * max77620_thermal_read_temp: Read PMIC die temperatue.
 35 * @data:	Device specific data.
 36 * temp:	Temperature in millidegrees Celsius
 37 *
 38 * The actual temperature of PMIC die is not available from PMIC.
 39 * PMIC only tells the status if it has crossed or not the threshold level
 40 * of 120degC or 140degC.
 41 * If threshold has not been crossed then assume die temperature as 100degC
 42 * else 120degC or 140deG based on the PMIC die temp threshold status.
 43 *
 44 * Return 0 on success otherwise error number to show reason of failure.
 45 */
 46
 47static int max77620_thermal_read_temp(void *data, int *temp)
 48{
 49	struct max77620_therm_info *mtherm = data;
 50	unsigned int val;
 51	int ret;
 52
 53	ret = regmap_read(mtherm->rmap, MAX77620_REG_STATLBT, &val);
 54	if (ret < 0) {
 55		dev_err(mtherm->dev, "Failed to read STATLBT: %d\n", ret);
 56		return ret;
 57	}
 58
 59	if (val & MAX77620_IRQ_TJALRM2_MASK)
 60		*temp = MAX77620_TJALARM2_TEMP;
 61	else if (val & MAX77620_IRQ_TJALRM1_MASK)
 62		*temp = MAX77620_TJALARM1_TEMP;
 63	else
 64		*temp = MAX77620_NORMAL_OPERATING_TEMP;
 65
 66	return 0;
 67}
 68
 69static const struct thermal_zone_of_device_ops max77620_thermal_ops = {
 70	.get_temp = max77620_thermal_read_temp,
 71};
 72
 73static irqreturn_t max77620_thermal_irq(int irq, void *data)
 74{
 75	struct max77620_therm_info *mtherm = data;
 76
 77	if (irq == mtherm->irq_tjalarm1)
 78		dev_warn(mtherm->dev, "Junction Temp Alarm1(120C) occurred\n");
 79	else if (irq == mtherm->irq_tjalarm2)
 80		dev_crit(mtherm->dev, "Junction Temp Alarm2(140C) occurred\n");
 81
 82	thermal_zone_device_update(mtherm->tz_device,
 83				   THERMAL_EVENT_UNSPECIFIED);
 84
 85	return IRQ_HANDLED;
 86}
 87
 88static int max77620_thermal_probe(struct platform_device *pdev)
 89{
 90	struct max77620_therm_info *mtherm;
 91	int ret;
 92
 93	mtherm = devm_kzalloc(&pdev->dev, sizeof(*mtherm), GFP_KERNEL);
 94	if (!mtherm)
 95		return -ENOMEM;
 96
 97	mtherm->irq_tjalarm1 = platform_get_irq(pdev, 0);
 98	mtherm->irq_tjalarm2 = platform_get_irq(pdev, 1);
 99	if ((mtherm->irq_tjalarm1 < 0) || (mtherm->irq_tjalarm2 < 0)) {
100		dev_err(&pdev->dev, "Alarm irq number not available\n");
101		return -EINVAL;
102	}
103
104	mtherm->dev = &pdev->dev;
105	mtherm->rmap = dev_get_regmap(pdev->dev.parent, NULL);
106	if (!mtherm->rmap) {
107		dev_err(&pdev->dev, "Failed to get parent regmap\n");
108		return -ENODEV;
109	}
110
111	/*
112	 * The reference taken to the parent's node which will be balanced on
113	 * reprobe or on platform-device release.
114	 */
115	device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent);
116
117	mtherm->tz_device = devm_thermal_zone_of_sensor_register(&pdev->dev, 0,
118				mtherm, &max77620_thermal_ops);
119	if (IS_ERR(mtherm->tz_device)) {
120		ret = PTR_ERR(mtherm->tz_device);
121		dev_err(&pdev->dev, "Failed to register thermal zone: %d\n",
122			ret);
123		return ret;
124	}
125
126	ret = devm_request_threaded_irq(&pdev->dev, mtherm->irq_tjalarm1, NULL,
127					max77620_thermal_irq,
128					IRQF_ONESHOT | IRQF_SHARED,
129					dev_name(&pdev->dev), mtherm);
130	if (ret < 0) {
131		dev_err(&pdev->dev, "Failed to request irq1: %d\n", ret);
132		return ret;
133	}
134
135	ret = devm_request_threaded_irq(&pdev->dev, mtherm->irq_tjalarm2, NULL,
136					max77620_thermal_irq,
137					IRQF_ONESHOT | IRQF_SHARED,
138					dev_name(&pdev->dev), mtherm);
139	if (ret < 0) {
140		dev_err(&pdev->dev, "Failed to request irq2: %d\n", ret);
141		return ret;
142	}
143
144	platform_set_drvdata(pdev, mtherm);
145
146	return 0;
147}
148
149static struct platform_device_id max77620_thermal_devtype[] = {
150	{ .name = "max77620-thermal", },
151	{},
152};
153MODULE_DEVICE_TABLE(platform, max77620_thermal_devtype);
154
155static struct platform_driver max77620_thermal_driver = {
156	.driver = {
157		.name = "max77620-thermal",
158	},
159	.probe = max77620_thermal_probe,
160	.id_table = max77620_thermal_devtype,
161};
162
163module_platform_driver(max77620_thermal_driver);
164
165MODULE_DESCRIPTION("Max77620 Junction temperature Thermal driver");
166MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
167MODULE_AUTHOR("Mallikarjun Kasoju <mkasoju@nvidia.com>");
168MODULE_LICENSE("GPL v2");