| .. | .. |
|---|
| 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, |
|---|