blob: 88bf457c566c0bb7b723b61d3d4d151c00040d13 [file] [log] [blame]
Sudheer Papothid889f1a2018-04-06 00:51:48 +05301/* Copyright (c) 2015, 2017-2018 The Linux Foundation. All rights reserved.
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05302 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/bitops.h>
14#include <linux/kernel.h>
Sudheer Papothid889f1a2018-04-06 00:51:48 +053015#include <linux/suspend.h>
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053016#include <linux/errno.h>
17#include <linux/delay.h>
18#include <linux/thermal.h>
19#include <sound/soc.h>
20#include "wsa881x-temp-sensor.h"
21
22#define T1_TEMP -10
23#define T2_TEMP 150
24#define LOW_TEMP_THRESHOLD 5
25#define HIGH_TEMP_THRESHOLD 45
26#define TEMP_INVALID 0xFFFF
27#define WSA881X_TEMP_RETRY 3
28/*
29 * wsa881x_get_temp - get wsa temperature
30 * @thermal: thermal zone device
31 * @temp: temperature value
32 *
33 * Get the temperature of wsa881x.
34 *
35 * Return: 0 on success or negative error code on failure.
36 */
37int wsa881x_get_temp(struct thermal_zone_device *thermal,
38 int *temp)
39{
40 struct wsa881x_tz_priv *pdata;
41 struct snd_soc_codec *codec;
42 struct wsa_temp_register reg;
43 int dmeas, d1, d2;
44 int ret = 0;
45 int temp_val;
46 int t1 = T1_TEMP;
47 int t2 = T2_TEMP;
48 u8 retry = WSA881X_TEMP_RETRY;
49
50 if (!thermal)
51 return -EINVAL;
52
53 if (thermal->devdata) {
54 pdata = thermal->devdata;
55 if (pdata->codec) {
56 codec = pdata->codec;
57 } else {
58 pr_err("%s: codec is NULL\n", __func__);
59 return -EINVAL;
60 }
61 } else {
62 pr_err("%s: pdata is NULL\n", __func__);
63 return -EINVAL;
64 }
Sudheer Papothid889f1a2018-04-06 00:51:48 +053065 if (atomic_cmpxchg(&pdata->is_suspend_spk, 1, 0)) {
66 /*
67 * get_temp query happens as part of POST_PM_SUSPEND
68 * from thermal core. To avoid calls to slimbus
69 * as part of this thermal query, return default temp
70 * and reset the suspend flag.
71 */
72 if (!pdata->t0_init) {
73 if (temp)
74 *temp = pdata->curr_temp;
75 return 0;
76 }
77 }
78
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053079temp_retry:
80 if (pdata->wsa_temp_reg_read) {
81 ret = pdata->wsa_temp_reg_read(codec, &reg);
82 if (ret) {
83 pr_err("%s: temperature register read failed: %d\n",
84 __func__, ret);
85 return ret;
86 }
87 } else {
88 pr_err("%s: wsa_temp_reg_read is NULL\n", __func__);
89 return -EINVAL;
90 }
91 /*
92 * Temperature register values are expected to be in the
93 * following range.
94 * d1_msb = 68 - 92 and d1_lsb = 0, 64, 128, 192
95 * d2_msb = 185 -218 and d2_lsb = 0, 64, 128, 192
96 */
97 if ((reg.d1_msb < 68 || reg.d1_msb > 92) ||
98 (!(reg.d1_lsb == 0 || reg.d1_lsb == 64 || reg.d1_lsb == 128 ||
99 reg.d1_lsb == 192)) ||
100 (reg.d2_msb < 185 || reg.d2_msb > 218) ||
101 (!(reg.d2_lsb == 0 || reg.d2_lsb == 64 || reg.d2_lsb == 128 ||
102 reg.d2_lsb == 192))) {
103 printk_ratelimited("%s: Temperature registers[%d %d %d %d] are out of range\n",
104 __func__, reg.d1_msb, reg.d1_lsb, reg.d2_msb,
105 reg.d2_lsb);
106 }
107 dmeas = ((reg.dmeas_msb << 0x8) | reg.dmeas_lsb) >> 0x6;
108 d1 = ((reg.d1_msb << 0x8) | reg.d1_lsb) >> 0x6;
109 d2 = ((reg.d2_msb << 0x8) | reg.d2_lsb) >> 0x6;
110
111 if (d1 == d2)
112 temp_val = TEMP_INVALID;
113 else
114 temp_val = t1 + (((dmeas - d1) * (t2 - t1))/(d2 - d1));
115
116 if (temp_val <= LOW_TEMP_THRESHOLD ||
117 temp_val >= HIGH_TEMP_THRESHOLD) {
118 printk_ratelimited("%s: T0: %d is out of range[%d, %d]\n",
119 __func__, temp_val, LOW_TEMP_THRESHOLD,
120 HIGH_TEMP_THRESHOLD);
121 if (retry--) {
122 msleep(20);
123 goto temp_retry;
124 }
125 }
Sudheer Papothid889f1a2018-04-06 00:51:48 +0530126 pdata->curr_temp = temp_val;
127
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530128 if (temp)
129 *temp = temp_val;
130 pr_debug("%s: t0 measured: %d dmeas = %d, d1 = %d, d2 = %d\n",
131 __func__, temp_val, dmeas, d1, d2);
132 return ret;
133}
134EXPORT_SYMBOL(wsa881x_get_temp);
135
136static struct thermal_zone_device_ops wsa881x_thermal_ops = {
137 .get_temp = wsa881x_get_temp,
138};
139
Sudheer Papothid889f1a2018-04-06 00:51:48 +0530140
141static int wsa881x_pm_notify(struct notifier_block *nb,
142 unsigned long mode, void *_unused)
143{
144 struct wsa881x_tz_priv *pdata =
145 container_of(nb, struct wsa881x_tz_priv, pm_nb);
146
147 switch (mode) {
148 case PM_SUSPEND_PREPARE:
149 case PM_HIBERNATION_PREPARE:
150 atomic_set(&pdata->is_suspend_spk, 1);
151 break;
152 default:
153 break;
154 }
155 return 0;
156}
157
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530158int wsa881x_init_thermal(struct wsa881x_tz_priv *tz_pdata)
159{
160 struct thermal_zone_device *tz_dev;
161
162 if (tz_pdata == NULL) {
163 pr_err("%s: thermal pdata is NULL\n", __func__);
164 return -EINVAL;
165 }
166 /* Register with the thermal zone */
167 tz_dev = thermal_zone_device_register(tz_pdata->name,
168 0, 0, tz_pdata,
169 &wsa881x_thermal_ops, NULL, 0, 0);
170 if (IS_ERR(tz_dev)) {
171 pr_err("%s: thermal device register failed.\n", __func__);
172 return -EINVAL;
173 }
174 tz_pdata->tz_dev = tz_dev;
Sudheer Papothid889f1a2018-04-06 00:51:48 +0530175 tz_pdata->pm_nb.notifier_call = wsa881x_pm_notify;
176 register_pm_notifier(&tz_pdata->pm_nb);
177 atomic_set(&tz_pdata->is_suspend_spk, 0);
178
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530179 return 0;
180}
181EXPORT_SYMBOL(wsa881x_init_thermal);
182
183void wsa881x_deinit_thermal(struct thermal_zone_device *tz_dev)
184{
Sudheer Papothid889f1a2018-04-06 00:51:48 +0530185 struct wsa881x_tz_priv *pdata;
186
187 if (tz_dev && tz_dev->devdata) {
188 pdata = tz_dev->devdata;
189 if (pdata)
190 unregister_pm_notifier(&pdata->pm_nb);
191 }
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530192 if (tz_dev)
193 thermal_zone_device_unregister(tz_dev);
194}
195EXPORT_SYMBOL(wsa881x_deinit_thermal);