.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Broadcom STB AVS TMON thermal sensor driver |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (c) 2015-2017 Broadcom |
---|
5 | | - * |
---|
6 | | - * This software is licensed under the terms of the GNU General Public |
---|
7 | | - * License version 2, as published by the Free Software Foundation, and |
---|
8 | | - * may be copied, distributed, and modified under those terms. |
---|
9 | | - * |
---|
10 | | - * This program is distributed in the hope that it will be useful, |
---|
11 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
12 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
13 | | - * GNU General Public License for more details. |
---|
14 | | - * |
---|
15 | 6 | */ |
---|
16 | 7 | |
---|
17 | 8 | #define DRV_NAME "brcmstb_thermal" |
---|
.. | .. |
---|
111 | 102 | }, |
---|
112 | 103 | }; |
---|
113 | 104 | |
---|
| 105 | +struct brcmstb_thermal_params { |
---|
| 106 | + unsigned int offset; |
---|
| 107 | + unsigned int mult; |
---|
| 108 | + const struct thermal_zone_of_device_ops *of_ops; |
---|
| 109 | +}; |
---|
| 110 | + |
---|
114 | 111 | struct brcmstb_thermal_priv { |
---|
115 | 112 | void __iomem *tmon_base; |
---|
116 | 113 | struct device *dev; |
---|
117 | 114 | struct thermal_zone_device *thermal; |
---|
| 115 | + /* Process specific thermal parameters used for calculations */ |
---|
| 116 | + const struct brcmstb_thermal_params *temp_params; |
---|
118 | 117 | }; |
---|
119 | 118 | |
---|
120 | 119 | /* Convert a HW code to a temperature reading (millidegree celsius) */ |
---|
121 | | -static inline int avs_tmon_code_to_temp(struct thermal_zone_device *tz, |
---|
| 120 | +static inline int avs_tmon_code_to_temp(struct brcmstb_thermal_priv *priv, |
---|
122 | 121 | u32 code) |
---|
123 | 122 | { |
---|
124 | | - return (AVS_TMON_TEMP_OFFSET - |
---|
125 | | - (int)((code & AVS_TMON_TEMP_MAX) * AVS_TMON_TEMP_SLOPE)); |
---|
| 123 | + int offset = priv->temp_params->offset; |
---|
| 124 | + int mult = priv->temp_params->mult; |
---|
| 125 | + |
---|
| 126 | + return (offset - (int)((code & AVS_TMON_TEMP_MASK) * mult)); |
---|
126 | 127 | } |
---|
127 | 128 | |
---|
128 | 129 | /* |
---|
.. | .. |
---|
131 | 132 | * @temp: temperature to convert |
---|
132 | 133 | * @low: if true, round toward the low side |
---|
133 | 134 | */ |
---|
134 | | -static inline u32 avs_tmon_temp_to_code(struct thermal_zone_device *tz, |
---|
| 135 | +static inline u32 avs_tmon_temp_to_code(struct brcmstb_thermal_priv *priv, |
---|
135 | 136 | int temp, bool low) |
---|
136 | 137 | { |
---|
| 138 | + int offset = priv->temp_params->offset; |
---|
| 139 | + int mult = priv->temp_params->mult; |
---|
| 140 | + |
---|
137 | 141 | if (temp < AVS_TMON_TEMP_MIN) |
---|
138 | 142 | return AVS_TMON_TEMP_MAX; /* Maximum code value */ |
---|
139 | 143 | |
---|
140 | | - if (temp >= AVS_TMON_TEMP_OFFSET) |
---|
| 144 | + if (temp >= offset) |
---|
141 | 145 | return 0; /* Minimum code value */ |
---|
142 | 146 | |
---|
143 | 147 | if (low) |
---|
144 | | - return (u32)(DIV_ROUND_UP(AVS_TMON_TEMP_OFFSET - temp, |
---|
145 | | - AVS_TMON_TEMP_SLOPE)); |
---|
| 148 | + return (u32)(DIV_ROUND_UP(offset - temp, mult)); |
---|
146 | 149 | else |
---|
147 | | - return (u32)((AVS_TMON_TEMP_OFFSET - temp) / |
---|
148 | | - AVS_TMON_TEMP_SLOPE); |
---|
| 150 | + return (u32)((offset - temp) / mult); |
---|
149 | 151 | } |
---|
150 | 152 | |
---|
151 | 153 | static int brcmstb_get_temp(void *data, int *temp) |
---|
.. | .. |
---|
163 | 165 | |
---|
164 | 166 | val = (val & AVS_TMON_STATUS_data_msk) >> AVS_TMON_STATUS_data_shift; |
---|
165 | 167 | |
---|
166 | | - t = avs_tmon_code_to_temp(priv->thermal, val); |
---|
| 168 | + t = avs_tmon_code_to_temp(priv, val); |
---|
167 | 169 | if (t < 0) |
---|
168 | 170 | *temp = 0; |
---|
169 | 171 | else |
---|
.. | .. |
---|
197 | 199 | val &= trip->reg_msk; |
---|
198 | 200 | val >>= trip->reg_shift; |
---|
199 | 201 | |
---|
200 | | - return avs_tmon_code_to_temp(priv->thermal, val); |
---|
| 202 | + return avs_tmon_code_to_temp(priv, val); |
---|
201 | 203 | } |
---|
202 | 204 | |
---|
203 | 205 | static void avs_tmon_set_trip_temp(struct brcmstb_thermal_priv *priv, |
---|
.. | .. |
---|
210 | 212 | dev_dbg(priv->dev, "set temp %d to %d\n", type, temp); |
---|
211 | 213 | |
---|
212 | 214 | /* round toward low temp for the low interrupt */ |
---|
213 | | - val = avs_tmon_temp_to_code(priv->thermal, temp, |
---|
| 215 | + val = avs_tmon_temp_to_code(priv, temp, |
---|
214 | 216 | type == TMON_TRIP_TYPE_LOW); |
---|
215 | 217 | |
---|
216 | 218 | val <<= trip->reg_shift; |
---|
.. | .. |
---|
227 | 229 | u32 val; |
---|
228 | 230 | |
---|
229 | 231 | val = __raw_readl(priv->tmon_base + AVS_TMON_TEMP_INT_CODE); |
---|
230 | | - return avs_tmon_code_to_temp(priv->thermal, val); |
---|
| 232 | + return avs_tmon_code_to_temp(priv, val); |
---|
231 | 233 | } |
---|
232 | 234 | |
---|
233 | 235 | static irqreturn_t brcmstb_tmon_irq_thread(int irq, void *data) |
---|
.. | .. |
---|
286 | 288 | return 0; |
---|
287 | 289 | } |
---|
288 | 290 | |
---|
289 | | -static struct thermal_zone_of_device_ops of_ops = { |
---|
| 291 | +static const struct thermal_zone_of_device_ops brcmstb_16nm_of_ops = { |
---|
| 292 | + .get_temp = brcmstb_get_temp, |
---|
| 293 | +}; |
---|
| 294 | + |
---|
| 295 | +static const struct brcmstb_thermal_params brcmstb_16nm_params = { |
---|
| 296 | + .offset = 457829, |
---|
| 297 | + .mult = 557, |
---|
| 298 | + .of_ops = &brcmstb_16nm_of_ops, |
---|
| 299 | +}; |
---|
| 300 | + |
---|
| 301 | +static const struct thermal_zone_of_device_ops brcmstb_28nm_of_ops = { |
---|
290 | 302 | .get_temp = brcmstb_get_temp, |
---|
291 | 303 | .set_trips = brcmstb_set_trips, |
---|
292 | 304 | }; |
---|
293 | 305 | |
---|
| 306 | +static const struct brcmstb_thermal_params brcmstb_28nm_params = { |
---|
| 307 | + .offset = 410040, |
---|
| 308 | + .mult = 487, |
---|
| 309 | + .of_ops = &brcmstb_28nm_of_ops, |
---|
| 310 | +}; |
---|
| 311 | + |
---|
294 | 312 | static const struct of_device_id brcmstb_thermal_id_table[] = { |
---|
295 | | - { .compatible = "brcm,avs-tmon" }, |
---|
| 313 | + { .compatible = "brcm,avs-tmon-bcm7216", .data = &brcmstb_16nm_params }, |
---|
| 314 | + { .compatible = "brcm,avs-tmon", .data = &brcmstb_28nm_params }, |
---|
296 | 315 | {}, |
---|
297 | 316 | }; |
---|
298 | 317 | MODULE_DEVICE_TABLE(of, brcmstb_thermal_id_table); |
---|
299 | 318 | |
---|
300 | 319 | static int brcmstb_thermal_probe(struct platform_device *pdev) |
---|
301 | 320 | { |
---|
| 321 | + const struct thermal_zone_of_device_ops *of_ops; |
---|
302 | 322 | struct thermal_zone_device *thermal; |
---|
303 | 323 | struct brcmstb_thermal_priv *priv; |
---|
304 | 324 | struct resource *res; |
---|
.. | .. |
---|
308 | 328 | if (!priv) |
---|
309 | 329 | return -ENOMEM; |
---|
310 | 330 | |
---|
| 331 | + priv->temp_params = of_device_get_match_data(&pdev->dev); |
---|
| 332 | + if (!priv->temp_params) |
---|
| 333 | + return -EINVAL; |
---|
| 334 | + |
---|
311 | 335 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
312 | 336 | priv->tmon_base = devm_ioremap_resource(&pdev->dev, res); |
---|
313 | 337 | if (IS_ERR(priv->tmon_base)) |
---|
.. | .. |
---|
315 | 339 | |
---|
316 | 340 | priv->dev = &pdev->dev; |
---|
317 | 341 | platform_set_drvdata(pdev, priv); |
---|
| 342 | + of_ops = priv->temp_params->of_ops; |
---|
318 | 343 | |
---|
319 | | - thermal = thermal_zone_of_sensor_register(&pdev->dev, 0, priv, &of_ops); |
---|
| 344 | + thermal = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, priv, |
---|
| 345 | + of_ops); |
---|
320 | 346 | if (IS_ERR(thermal)) { |
---|
321 | 347 | ret = PTR_ERR(thermal); |
---|
322 | 348 | dev_err(&pdev->dev, "could not register sensor: %d\n", ret); |
---|
.. | .. |
---|
326 | 352 | priv->thermal = thermal; |
---|
327 | 353 | |
---|
328 | 354 | irq = platform_get_irq(pdev, 0); |
---|
329 | | - if (irq < 0) { |
---|
330 | | - dev_err(&pdev->dev, "could not get IRQ\n"); |
---|
331 | | - ret = irq; |
---|
332 | | - goto err; |
---|
333 | | - } |
---|
334 | | - ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, |
---|
335 | | - brcmstb_tmon_irq_thread, IRQF_ONESHOT, |
---|
336 | | - DRV_NAME, priv); |
---|
337 | | - if (ret < 0) { |
---|
338 | | - dev_err(&pdev->dev, "could not request IRQ: %d\n", ret); |
---|
339 | | - goto err; |
---|
| 355 | + if (irq >= 0) { |
---|
| 356 | + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, |
---|
| 357 | + brcmstb_tmon_irq_thread, |
---|
| 358 | + IRQF_ONESHOT, |
---|
| 359 | + DRV_NAME, priv); |
---|
| 360 | + if (ret < 0) { |
---|
| 361 | + dev_err(&pdev->dev, "could not request IRQ: %d\n", ret); |
---|
| 362 | + return ret; |
---|
| 363 | + } |
---|
340 | 364 | } |
---|
341 | 365 | |
---|
342 | 366 | dev_info(&pdev->dev, "registered AVS TMON of-sensor driver\n"); |
---|
343 | | - |
---|
344 | | - return 0; |
---|
345 | | - |
---|
346 | | -err: |
---|
347 | | - thermal_zone_of_sensor_unregister(&pdev->dev, thermal); |
---|
348 | | - return ret; |
---|
349 | | -} |
---|
350 | | - |
---|
351 | | -static int brcmstb_thermal_exit(struct platform_device *pdev) |
---|
352 | | -{ |
---|
353 | | - struct brcmstb_thermal_priv *priv = platform_get_drvdata(pdev); |
---|
354 | | - struct thermal_zone_device *thermal = priv->thermal; |
---|
355 | | - |
---|
356 | | - if (thermal) |
---|
357 | | - thermal_zone_of_sensor_unregister(&pdev->dev, priv->thermal); |
---|
358 | 367 | |
---|
359 | 368 | return 0; |
---|
360 | 369 | } |
---|
361 | 370 | |
---|
362 | 371 | static struct platform_driver brcmstb_thermal_driver = { |
---|
363 | 372 | .probe = brcmstb_thermal_probe, |
---|
364 | | - .remove = brcmstb_thermal_exit, |
---|
365 | 373 | .driver = { |
---|
366 | 374 | .name = DRV_NAME, |
---|
367 | 375 | .of_match_table = brcmstb_thermal_id_table, |
---|