148b2bce8SKeerthy // SPDX-License-Identifier: GPL-2.0
248b2bce8SKeerthy /*
348b2bce8SKeerthy * TI Bandgap temperature sensor driver for K3 SoC Family
448b2bce8SKeerthy *
548b2bce8SKeerthy * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
648b2bce8SKeerthy */
748b2bce8SKeerthy
848b2bce8SKeerthy #include <linux/err.h>
948b2bce8SKeerthy #include <linux/init.h>
1048b2bce8SKeerthy #include <linux/io.h>
1148b2bce8SKeerthy #include <linux/kernel.h>
1248b2bce8SKeerthy #include <linux/module.h>
1348b2bce8SKeerthy #include <linux/of.h>
14*f6a756e8SRob Herring #include <linux/platform_device.h>
1548b2bce8SKeerthy #include <linux/pm_runtime.h>
1648b2bce8SKeerthy #include <linux/thermal.h>
1748b2bce8SKeerthy #include <linux/types.h>
1848b2bce8SKeerthy
199e5f5f15SMassimiliano Minella #include "thermal_hwmon.h"
209e5f5f15SMassimiliano Minella
2148b2bce8SKeerthy #define K3_VTM_DEVINFO_PWR0_OFFSET 0x4
2248b2bce8SKeerthy #define K3_VTM_DEVINFO_PWR0_TEMPSENS_CT_MASK 0xf0
2348b2bce8SKeerthy #define K3_VTM_TMPSENS0_CTRL_OFFSET 0x80
2448b2bce8SKeerthy #define K3_VTM_REGS_PER_TS 0x10
2548b2bce8SKeerthy #define K3_VTM_TS_STAT_DTEMP_MASK 0x3ff
2648b2bce8SKeerthy #define K3_VTM_TMPSENS_CTRL_CBIASSEL BIT(0)
2748b2bce8SKeerthy #define K3_VTM_TMPSENS_CTRL_SOC BIT(5)
2848b2bce8SKeerthy #define K3_VTM_TMPSENS_CTRL_CLRZ BIT(6)
2948b2bce8SKeerthy #define K3_VTM_TMPSENS_CTRL_CLKON_REQ BIT(7)
3048b2bce8SKeerthy
3148b2bce8SKeerthy #define K3_VTM_ADC_BEGIN_VAL 540
3248b2bce8SKeerthy #define K3_VTM_ADC_END_VAL 944
3348b2bce8SKeerthy
3448b2bce8SKeerthy static const int k3_adc_to_temp[] = {
3548b2bce8SKeerthy -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600, -38200,
3648b2bce8SKeerthy -37800, -37400, -37000, -36600, -36200, -35800, -35300, -34700, -34200,
3748b2bce8SKeerthy -33800, -33400, -33000, -32600, -32200, -31800, -31400, -31000, -30600,
3848b2bce8SKeerthy -30200, -29800, -29400, -29000, -28600, -28200, -27700, -27100, -26600,
3948b2bce8SKeerthy -26200, -25800, -25400, -25000, -24600, -24200, -23800, -23400, -23000,
4048b2bce8SKeerthy -22600, -22200, -21800, -21400, -21000, -20500, -19900, -19400, -19000,
4148b2bce8SKeerthy -18600, -18200, -17800, -17400, -17000, -16600, -16200, -15800, -15400,
4248b2bce8SKeerthy -15000, -14600, -14200, -13800, -13400, -13000, -12500, -11900, -11400,
4348b2bce8SKeerthy -11000, -10600, -10200, -9800, -9400, -9000, -8600, -8200, -7800, -7400,
4448b2bce8SKeerthy -7000, -6600, -6200, -5800, -5400, -5000, -4500, -3900, -3400, -3000,
4548b2bce8SKeerthy -2600, -2200, -1800, -1400, -1000, -600, -200, 200, 600, 1000, 1400,
4648b2bce8SKeerthy 1800, 2200, 2600, 3000, 3400, 3900, 4500, 5000, 5400, 5800, 6200, 6600,
4748b2bce8SKeerthy 7000, 7400, 7800, 8200, 8600, 9000, 9400, 9800, 10200, 10600, 11000,
4848b2bce8SKeerthy 11400, 11800, 12200, 12700, 13300, 13800, 14200, 14600, 15000, 15400,
4948b2bce8SKeerthy 15800, 16200, 16600, 17000, 17400, 17800, 18200, 18600, 19000, 19400,
5048b2bce8SKeerthy 19800, 20200, 20600, 21000, 21400, 21900, 22500, 23000, 23400, 23800,
5148b2bce8SKeerthy 24200, 24600, 25000, 25400, 25800, 26200, 26600, 27000, 27400, 27800,
5248b2bce8SKeerthy 28200, 28600, 29000, 29400, 29800, 30200, 30600, 31000, 31400, 31900,
5348b2bce8SKeerthy 32500, 33000, 33400, 33800, 34200, 34600, 35000, 35400, 35800, 36200,
5448b2bce8SKeerthy 36600, 37000, 37400, 37800, 38200, 38600, 39000, 39400, 39800, 40200,
5548b2bce8SKeerthy 40600, 41000, 41400, 41800, 42200, 42600, 43100, 43700, 44200, 44600,
5648b2bce8SKeerthy 45000, 45400, 45800, 46200, 46600, 47000, 47400, 47800, 48200, 48600,
5748b2bce8SKeerthy 49000, 49400, 49800, 50200, 50600, 51000, 51400, 51800, 52200, 52600,
5848b2bce8SKeerthy 53000, 53400, 53800, 54200, 54600, 55000, 55400, 55900, 56500, 57000,
5948b2bce8SKeerthy 57400, 57800, 58200, 58600, 59000, 59400, 59800, 60200, 60600, 61000,
6048b2bce8SKeerthy 61400, 61800, 62200, 62600, 63000, 63400, 63800, 64200, 64600, 65000,
6148b2bce8SKeerthy 65400, 65800, 66200, 66600, 67000, 67400, 67800, 68200, 68600, 69000,
6248b2bce8SKeerthy 69400, 69800, 70200, 70600, 71000, 71500, 72100, 72600, 73000, 73400,
6348b2bce8SKeerthy 73800, 74200, 74600, 75000, 75400, 75800, 76200, 76600, 77000, 77400,
6448b2bce8SKeerthy 77800, 78200, 78600, 79000, 79400, 79800, 80200, 80600, 81000, 81400,
6548b2bce8SKeerthy 81800, 82200, 82600, 83000, 83400, 83800, 84200, 84600, 85000, 85400,
6648b2bce8SKeerthy 85800, 86200, 86600, 87000, 87400, 87800, 88200, 88600, 89000, 89400,
6748b2bce8SKeerthy 89800, 90200, 90600, 91000, 91400, 91800, 92200, 92600, 93000, 93400,
6848b2bce8SKeerthy 93800, 94200, 94600, 95000, 95400, 95800, 96200, 96600, 97000, 97500,
6948b2bce8SKeerthy 98100, 98600, 99000, 99400, 99800, 100200, 100600, 101000, 101400,
7048b2bce8SKeerthy 101800, 102200, 102600, 103000, 103400, 103800, 104200, 104600, 105000,
7148b2bce8SKeerthy 105400, 105800, 106200, 106600, 107000, 107400, 107800, 108200, 108600,
7248b2bce8SKeerthy 109000, 109400, 109800, 110200, 110600, 111000, 111400, 111800, 112200,
7348b2bce8SKeerthy 112600, 113000, 113400, 113800, 114200, 114600, 115000, 115400, 115800,
7448b2bce8SKeerthy 116200, 116600, 117000, 117400, 117800, 118200, 118600, 119000, 119400,
7548b2bce8SKeerthy 119800, 120200, 120600, 121000, 121400, 121800, 122200, 122600, 123000,
7648b2bce8SKeerthy 123400, 123800, 124200, 124600, 124900, 125000,
7748b2bce8SKeerthy };
7848b2bce8SKeerthy
7948b2bce8SKeerthy struct k3_bandgap {
8048b2bce8SKeerthy void __iomem *base;
8148b2bce8SKeerthy const struct k3_bandgap_data *conf;
8248b2bce8SKeerthy };
8348b2bce8SKeerthy
8448b2bce8SKeerthy /* common data structures */
8548b2bce8SKeerthy struct k3_thermal_data {
8648b2bce8SKeerthy struct thermal_zone_device *tzd;
8748b2bce8SKeerthy struct k3_bandgap *bgp;
8848b2bce8SKeerthy int sensor_id;
8948b2bce8SKeerthy u32 ctrl_offset;
9048b2bce8SKeerthy u32 stat_offset;
9148b2bce8SKeerthy };
9248b2bce8SKeerthy
vtm_get_best_value(unsigned int s0,unsigned int s1,unsigned int s2)9348b2bce8SKeerthy static unsigned int vtm_get_best_value(unsigned int s0, unsigned int s1,
9448b2bce8SKeerthy unsigned int s2)
9548b2bce8SKeerthy {
9648b2bce8SKeerthy int d01 = abs(s0 - s1);
9748b2bce8SKeerthy int d02 = abs(s0 - s2);
9848b2bce8SKeerthy int d12 = abs(s1 - s2);
9948b2bce8SKeerthy
10048b2bce8SKeerthy if (d01 <= d02 && d01 <= d12)
10148b2bce8SKeerthy return (s0 + s1) / 2;
10248b2bce8SKeerthy
10348b2bce8SKeerthy if (d02 <= d01 && d02 <= d12)
10448b2bce8SKeerthy return (s0 + s2) / 2;
10548b2bce8SKeerthy
10648b2bce8SKeerthy return (s1 + s2) / 2;
10748b2bce8SKeerthy }
10848b2bce8SKeerthy
k3_bgp_read_temp(struct k3_thermal_data * devdata,int * temp)10948b2bce8SKeerthy static int k3_bgp_read_temp(struct k3_thermal_data *devdata,
11048b2bce8SKeerthy int *temp)
11148b2bce8SKeerthy {
11248b2bce8SKeerthy struct k3_bandgap *bgp;
11348b2bce8SKeerthy unsigned int dtemp, s0, s1, s2;
11448b2bce8SKeerthy
11548b2bce8SKeerthy bgp = devdata->bgp;
11648b2bce8SKeerthy
11748b2bce8SKeerthy /*
11848b2bce8SKeerthy * Errata is applicable for am654 pg 1.0 silicon. There
11948b2bce8SKeerthy * is a variation of the order for 8-10 degree centigrade.
12048b2bce8SKeerthy * Work around that by getting the average of two closest
12148b2bce8SKeerthy * readings out of three readings everytime we want to
12248b2bce8SKeerthy * report temperatures.
12348b2bce8SKeerthy *
12448b2bce8SKeerthy * Errata workaround.
12548b2bce8SKeerthy */
12648b2bce8SKeerthy s0 = readl(bgp->base + devdata->stat_offset) &
12748b2bce8SKeerthy K3_VTM_TS_STAT_DTEMP_MASK;
12848b2bce8SKeerthy s1 = readl(bgp->base + devdata->stat_offset) &
12948b2bce8SKeerthy K3_VTM_TS_STAT_DTEMP_MASK;
13048b2bce8SKeerthy s2 = readl(bgp->base + devdata->stat_offset) &
13148b2bce8SKeerthy K3_VTM_TS_STAT_DTEMP_MASK;
13248b2bce8SKeerthy dtemp = vtm_get_best_value(s0, s1, s2);
13348b2bce8SKeerthy
13448b2bce8SKeerthy if (dtemp < K3_VTM_ADC_BEGIN_VAL || dtemp > K3_VTM_ADC_END_VAL)
13548b2bce8SKeerthy return -EINVAL;
13648b2bce8SKeerthy
13748b2bce8SKeerthy *temp = k3_adc_to_temp[dtemp - K3_VTM_ADC_BEGIN_VAL];
13848b2bce8SKeerthy
13948b2bce8SKeerthy return 0;
14048b2bce8SKeerthy }
14148b2bce8SKeerthy
k3_thermal_get_temp(struct thermal_zone_device * tz,int * temp)142b86105edSDaniel Lezcano static int k3_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
14348b2bce8SKeerthy {
1445f68d078SDaniel Lezcano struct k3_thermal_data *data = thermal_zone_device_priv(tz);
14548b2bce8SKeerthy int ret = 0;
14648b2bce8SKeerthy
14748b2bce8SKeerthy ret = k3_bgp_read_temp(data, temp);
14848b2bce8SKeerthy if (ret)
14948b2bce8SKeerthy return ret;
15048b2bce8SKeerthy
15148b2bce8SKeerthy return ret;
15248b2bce8SKeerthy }
15348b2bce8SKeerthy
154b86105edSDaniel Lezcano static const struct thermal_zone_device_ops k3_of_thermal_ops = {
15548b2bce8SKeerthy .get_temp = k3_thermal_get_temp,
15648b2bce8SKeerthy };
15748b2bce8SKeerthy
15848b2bce8SKeerthy static const struct of_device_id of_k3_bandgap_match[];
15948b2bce8SKeerthy
k3_bandgap_probe(struct platform_device * pdev)16048b2bce8SKeerthy static int k3_bandgap_probe(struct platform_device *pdev)
16148b2bce8SKeerthy {
16248b2bce8SKeerthy int ret = 0, cnt, val, id;
16348b2bce8SKeerthy struct resource *res;
16448b2bce8SKeerthy struct device *dev = &pdev->dev;
16548b2bce8SKeerthy struct k3_bandgap *bgp;
16648b2bce8SKeerthy struct k3_thermal_data *data;
16748b2bce8SKeerthy
16848b2bce8SKeerthy if (ARRAY_SIZE(k3_adc_to_temp) != (K3_VTM_ADC_END_VAL + 1 -
16948b2bce8SKeerthy K3_VTM_ADC_BEGIN_VAL))
17048b2bce8SKeerthy return -EINVAL;
17148b2bce8SKeerthy
17248b2bce8SKeerthy bgp = devm_kzalloc(&pdev->dev, sizeof(*bgp), GFP_KERNEL);
17348b2bce8SKeerthy if (!bgp)
17448b2bce8SKeerthy return -ENOMEM;
17548b2bce8SKeerthy
17648b2bce8SKeerthy res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
17748b2bce8SKeerthy bgp->base = devm_ioremap_resource(dev, res);
17848b2bce8SKeerthy if (IS_ERR(bgp->base))
17948b2bce8SKeerthy return PTR_ERR(bgp->base);
18048b2bce8SKeerthy
18148b2bce8SKeerthy pm_runtime_enable(dev);
18248b2bce8SKeerthy ret = pm_runtime_get_sync(dev);
18348b2bce8SKeerthy if (ret < 0) {
18448b2bce8SKeerthy pm_runtime_put_noidle(dev);
18548b2bce8SKeerthy pm_runtime_disable(dev);
18648b2bce8SKeerthy return ret;
18748b2bce8SKeerthy }
18848b2bce8SKeerthy
18948b2bce8SKeerthy /* Get the sensor count in the VTM */
19048b2bce8SKeerthy val = readl(bgp->base + K3_VTM_DEVINFO_PWR0_OFFSET);
19148b2bce8SKeerthy cnt = val & K3_VTM_DEVINFO_PWR0_TEMPSENS_CT_MASK;
19248b2bce8SKeerthy cnt >>= __ffs(K3_VTM_DEVINFO_PWR0_TEMPSENS_CT_MASK);
19348b2bce8SKeerthy
19448b2bce8SKeerthy data = devm_kcalloc(dev, cnt, sizeof(*data), GFP_KERNEL);
19548b2bce8SKeerthy if (!data) {
19648b2bce8SKeerthy ret = -ENOMEM;
19748b2bce8SKeerthy goto err_alloc;
19848b2bce8SKeerthy }
19948b2bce8SKeerthy
20048b2bce8SKeerthy /* Register the thermal sensors */
20148b2bce8SKeerthy for (id = 0; id < cnt; id++) {
20248b2bce8SKeerthy data[id].sensor_id = id;
20348b2bce8SKeerthy data[id].bgp = bgp;
20448b2bce8SKeerthy data[id].ctrl_offset = K3_VTM_TMPSENS0_CTRL_OFFSET +
20548b2bce8SKeerthy id * K3_VTM_REGS_PER_TS;
20648b2bce8SKeerthy data[id].stat_offset = data[id].ctrl_offset + 0x8;
20748b2bce8SKeerthy
20848b2bce8SKeerthy val = readl(data[id].bgp->base + data[id].ctrl_offset);
20948b2bce8SKeerthy val |= (K3_VTM_TMPSENS_CTRL_SOC |
21048b2bce8SKeerthy K3_VTM_TMPSENS_CTRL_CLRZ |
21148b2bce8SKeerthy K3_VTM_TMPSENS_CTRL_CLKON_REQ);
21248b2bce8SKeerthy val &= ~K3_VTM_TMPSENS_CTRL_CBIASSEL;
21348b2bce8SKeerthy writel(val, data[id].bgp->base + data[id].ctrl_offset);
21448b2bce8SKeerthy
21548b2bce8SKeerthy data[id].tzd =
216b86105edSDaniel Lezcano devm_thermal_of_zone_register(dev, id,
21748b2bce8SKeerthy &data[id],
21848b2bce8SKeerthy &k3_of_thermal_ops);
21948b2bce8SKeerthy if (IS_ERR(data[id].tzd)) {
22048b2bce8SKeerthy dev_err(dev, "thermal zone device is NULL\n");
22148b2bce8SKeerthy ret = PTR_ERR(data[id].tzd);
22248b2bce8SKeerthy goto err_alloc;
22348b2bce8SKeerthy }
2249e5f5f15SMassimiliano Minella
2257c673ef5SYangtao Li devm_thermal_add_hwmon_sysfs(dev, data[id].tzd);
22648b2bce8SKeerthy }
22748b2bce8SKeerthy
22848b2bce8SKeerthy
22948b2bce8SKeerthy return 0;
23048b2bce8SKeerthy
23148b2bce8SKeerthy err_alloc:
23248b2bce8SKeerthy pm_runtime_put_sync(dev);
23348b2bce8SKeerthy pm_runtime_disable(dev);
23448b2bce8SKeerthy
23548b2bce8SKeerthy return ret;
23648b2bce8SKeerthy }
23748b2bce8SKeerthy
k3_bandgap_remove(struct platform_device * pdev)23848b2bce8SKeerthy static int k3_bandgap_remove(struct platform_device *pdev)
23948b2bce8SKeerthy {
24048b2bce8SKeerthy pm_runtime_put_sync(&pdev->dev);
24148b2bce8SKeerthy pm_runtime_disable(&pdev->dev);
24248b2bce8SKeerthy
24348b2bce8SKeerthy return 0;
24448b2bce8SKeerthy }
24548b2bce8SKeerthy
24648b2bce8SKeerthy static const struct of_device_id of_k3_bandgap_match[] = {
24748b2bce8SKeerthy {
24848b2bce8SKeerthy .compatible = "ti,am654-vtm",
24948b2bce8SKeerthy },
25048b2bce8SKeerthy { /* sentinel */ },
25148b2bce8SKeerthy };
25248b2bce8SKeerthy MODULE_DEVICE_TABLE(of, of_k3_bandgap_match);
25348b2bce8SKeerthy
25448b2bce8SKeerthy static struct platform_driver k3_bandgap_sensor_driver = {
25548b2bce8SKeerthy .probe = k3_bandgap_probe,
25648b2bce8SKeerthy .remove = k3_bandgap_remove,
25748b2bce8SKeerthy .driver = {
25848b2bce8SKeerthy .name = "k3-soc-thermal",
25948b2bce8SKeerthy .of_match_table = of_k3_bandgap_match,
26048b2bce8SKeerthy },
26148b2bce8SKeerthy };
26248b2bce8SKeerthy
26348b2bce8SKeerthy module_platform_driver(k3_bandgap_sensor_driver);
26448b2bce8SKeerthy
26548b2bce8SKeerthy MODULE_DESCRIPTION("K3 bandgap temperature sensor driver");
26648b2bce8SKeerthy MODULE_LICENSE("GPL v2");
26748b2bce8SKeerthy MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>");
268