]>
Commit | Line | Data |
---|---|---|
2d71d8de | 1 | // SPDX-License-Identifier: GPL-2.0 |
9066073c RN |
2 | /* |
3 | * Copyright (c) 2015, The Linux Foundation. All rights reserved. | |
9066073c RN |
4 | */ |
5 | ||
6 | #include <linux/err.h> | |
7 | #include <linux/io.h> | |
8 | #include <linux/nvmem-consumer.h> | |
9 | #include <linux/of_address.h> | |
5b128398 | 10 | #include <linux/of_platform.h> |
9066073c RN |
11 | #include <linux/platform_device.h> |
12 | #include <linux/regmap.h> | |
13 | #include "tsens.h" | |
14 | ||
9066073c RN |
15 | char *qfprom_read(struct device *dev, const char *cname) |
16 | { | |
17 | struct nvmem_cell *cell; | |
18 | ssize_t data; | |
19 | char *ret; | |
20 | ||
21 | cell = nvmem_cell_get(dev, cname); | |
22 | if (IS_ERR(cell)) | |
23 | return ERR_CAST(cell); | |
24 | ||
25 | ret = nvmem_cell_read(cell, &data); | |
26 | nvmem_cell_put(cell); | |
27 | ||
28 | return ret; | |
29 | } | |
30 | ||
31 | /* | |
32 | * Use this function on devices where slope and offset calculations | |
33 | * depend on calibration data read from qfprom. On others the slope | |
34 | * and offset values are derived from tz->tzp->slope and tz->tzp->offset | |
35 | * resp. | |
36 | */ | |
69b628ac | 37 | void compute_intercept_slope(struct tsens_priv *priv, u32 *p1, |
9066073c RN |
38 | u32 *p2, u32 mode) |
39 | { | |
40 | int i; | |
41 | int num, den; | |
42 | ||
69b628ac AK |
43 | for (i = 0; i < priv->num_sensors; i++) { |
44 | dev_dbg(priv->dev, | |
9066073c RN |
45 | "sensor%d - data_point1:%#x data_point2:%#x\n", |
46 | i, p1[i], p2[i]); | |
47 | ||
69b628ac | 48 | priv->sensor[i].slope = SLOPE_DEFAULT; |
9066073c RN |
49 | if (mode == TWO_PT_CALIB) { |
50 | /* | |
51 | * slope (m) = adc_code2 - adc_code1 (y2 - y1)/ | |
52 | * temp_120_degc - temp_30_degc (x2 - x1) | |
53 | */ | |
54 | num = p2[i] - p1[i]; | |
55 | num *= SLOPE_FACTOR; | |
56 | den = CAL_DEGC_PT2 - CAL_DEGC_PT1; | |
69b628ac | 57 | priv->sensor[i].slope = num / den; |
9066073c RN |
58 | } |
59 | ||
69b628ac | 60 | priv->sensor[i].offset = (p1[i] * SLOPE_FACTOR) - |
9066073c | 61 | (CAL_DEGC_PT1 * |
69b628ac AK |
62 | priv->sensor[i].slope); |
63 | dev_dbg(priv->dev, "offset:%d\n", priv->sensor[i].offset); | |
9066073c RN |
64 | } |
65 | } | |
66 | ||
3e6a8fb3 AK |
67 | bool is_sensor_enabled(struct tsens_priv *priv, u32 hw_id) |
68 | { | |
69 | u32 val; | |
70 | int ret; | |
71 | ||
72 | if ((hw_id > (priv->num_sensors - 1)) || (hw_id < 0)) | |
73 | return -EINVAL; | |
74 | ret = regmap_field_read(priv->rf[SENSOR_EN], &val); | |
75 | if (ret) | |
76 | return ret; | |
77 | ||
78 | return val & (1 << hw_id); | |
79 | } | |
80 | ||
9066073c RN |
81 | static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s) |
82 | { | |
83 | int degc, num, den; | |
84 | ||
85 | num = (adc_code * SLOPE_FACTOR) - s->offset; | |
86 | den = s->slope; | |
87 | ||
88 | if (num > 0) | |
89 | degc = num + (den / 2); | |
90 | else if (num < 0) | |
91 | degc = num - (den / 2); | |
92 | else | |
93 | degc = num; | |
94 | ||
95 | degc /= den; | |
96 | ||
97 | return degc; | |
98 | } | |
99 | ||
c8b61690 AK |
100 | int get_temp_tsens_valid(struct tsens_priv *priv, int i, int *temp) |
101 | { | |
102 | struct tsens_sensor *s = &priv->sensor[i]; | |
103 | u32 temp_idx = LAST_TEMP_0 + s->hw_id; | |
104 | u32 valid_idx = VALID_0 + s->hw_id; | |
105 | u32 last_temp = 0, valid, mask; | |
106 | int ret; | |
107 | ||
108 | ret = regmap_field_read(priv->rf[valid_idx], &valid); | |
109 | if (ret) | |
110 | return ret; | |
111 | while (!valid) { | |
112 | /* Valid bit is 0 for 6 AHB clock cycles. | |
113 | * At 19.2MHz, 1 AHB clock is ~60ns. | |
114 | * We should enter this loop very, very rarely. | |
115 | */ | |
116 | ndelay(400); | |
117 | ret = regmap_field_read(priv->rf[valid_idx], &valid); | |
118 | if (ret) | |
119 | return ret; | |
120 | } | |
121 | ||
122 | /* Valid bit is set, OK to read the temperature */ | |
123 | ret = regmap_field_read(priv->rf[temp_idx], &last_temp); | |
124 | if (ret) | |
125 | return ret; | |
126 | ||
14bbe988 AK |
127 | if (priv->feat->adc) { |
128 | /* Convert temperature from ADC code to milliCelsius */ | |
129 | *temp = code_to_degc(last_temp, s) * 1000; | |
130 | } else { | |
131 | mask = GENMASK(priv->fields[LAST_TEMP_0].msb, | |
132 | priv->fields[LAST_TEMP_0].lsb); | |
133 | /* Convert temperature from deciCelsius to milliCelsius */ | |
134 | *temp = sign_extend32(last_temp, fls(mask) - 1) * 100; | |
135 | } | |
c8b61690 AK |
136 | |
137 | return 0; | |
138 | } | |
139 | ||
c1997054 | 140 | int get_temp_common(struct tsens_priv *priv, int i, int *temp) |
9066073c | 141 | { |
c1997054 | 142 | struct tsens_sensor *s = &priv->sensor[i]; |
9066073c RN |
143 | int last_temp = 0, ret; |
144 | ||
c1997054 | 145 | ret = regmap_field_read(priv->rf[LAST_TEMP_0 + s->hw_id], &last_temp); |
9066073c RN |
146 | if (ret) |
147 | return ret; | |
9066073c RN |
148 | |
149 | *temp = code_to_degc(last_temp, s) * 1000; | |
150 | ||
151 | return 0; | |
152 | } | |
153 | ||
154 | static const struct regmap_config tsens_config = { | |
4ab248b3 SK |
155 | .name = "tm", |
156 | .reg_bits = 32, | |
157 | .val_bits = 32, | |
158 | .reg_stride = 4, | |
159 | }; | |
160 | ||
161 | static const struct regmap_config tsens_srot_config = { | |
162 | .name = "srot", | |
9066073c RN |
163 | .reg_bits = 32, |
164 | .val_bits = 32, | |
165 | .reg_stride = 4, | |
166 | }; | |
167 | ||
69b628ac | 168 | int __init init_common(struct tsens_priv *priv) |
9066073c | 169 | { |
a15525b5 | 170 | void __iomem *tm_base, *srot_base; |
52eafd66 | 171 | struct device *dev = priv->dev; |
faa590ba | 172 | struct resource *res; |
c1997054 AK |
173 | u32 enabled; |
174 | int ret, i, j; | |
69b628ac | 175 | struct platform_device *op = of_find_device_by_node(priv->dev->of_node); |
9066073c | 176 | |
5b128398 AK |
177 | if (!op) |
178 | return -EINVAL; | |
9066073c | 179 | |
5b128398 | 180 | if (op->num_resources > 1) { |
a15525b5 | 181 | /* DT with separate SROT and TM address space */ |
69b628ac | 182 | priv->tm_offset = 0; |
a15525b5 AK |
183 | res = platform_get_resource(op, IORESOURCE_MEM, 1); |
184 | srot_base = devm_ioremap_resource(&op->dev, res); | |
a245b62b PH |
185 | if (IS_ERR(srot_base)) { |
186 | ret = PTR_ERR(srot_base); | |
187 | goto err_put_device; | |
188 | } | |
a15525b5 | 189 | |
52eafd66 | 190 | priv->srot_map = devm_regmap_init_mmio(dev, srot_base, |
4ab248b3 | 191 | &tsens_srot_config); |
69b628ac AK |
192 | if (IS_ERR(priv->srot_map)) { |
193 | ret = PTR_ERR(priv->srot_map); | |
a245b62b PH |
194 | goto err_put_device; |
195 | } | |
5b128398 AK |
196 | } else { |
197 | /* old DTs where SROT and TM were in a contiguous 2K block */ | |
69b628ac | 198 | priv->tm_offset = 0x1000; |
5b128398 AK |
199 | } |
200 | ||
faa590ba | 201 | res = platform_get_resource(op, IORESOURCE_MEM, 0); |
67b0f5e0 | 202 | tm_base = devm_ioremap_resource(&op->dev, res); |
a245b62b PH |
203 | if (IS_ERR(tm_base)) { |
204 | ret = PTR_ERR(tm_base); | |
205 | goto err_put_device; | |
206 | } | |
faa590ba | 207 | |
52eafd66 | 208 | priv->tm_map = devm_regmap_init_mmio(dev, tm_base, &tsens_config); |
69b628ac AK |
209 | if (IS_ERR(priv->tm_map)) { |
210 | ret = PTR_ERR(priv->tm_map); | |
a245b62b PH |
211 | goto err_put_device; |
212 | } | |
9066073c | 213 | |
52eafd66 | 214 | priv->rf[TSENS_EN] = devm_regmap_field_alloc(dev, priv->srot_map, |
c1997054 AK |
215 | priv->fields[TSENS_EN]); |
216 | if (IS_ERR(priv->rf[TSENS_EN])) { | |
217 | ret = PTR_ERR(priv->rf[TSENS_EN]); | |
218 | goto err_put_device; | |
219 | } | |
220 | ret = regmap_field_read(priv->rf[TSENS_EN], &enabled); | |
221 | if (ret) | |
222 | goto err_put_device; | |
223 | if (!enabled) { | |
52eafd66 | 224 | dev_err(dev, "tsens device is not enabled\n"); |
c1997054 AK |
225 | ret = -ENODEV; |
226 | goto err_put_device; | |
227 | } | |
228 | ||
52eafd66 | 229 | priv->rf[SENSOR_EN] = devm_regmap_field_alloc(dev, priv->srot_map, |
c1997054 AK |
230 | priv->fields[SENSOR_EN]); |
231 | if (IS_ERR(priv->rf[SENSOR_EN])) { | |
232 | ret = PTR_ERR(priv->rf[SENSOR_EN]); | |
233 | goto err_put_device; | |
234 | } | |
235 | /* now alloc regmap_fields in tm_map */ | |
1b6e3e51 | 236 | for (i = 0, j = LAST_TEMP_0; i < priv->feat->max_sensors; i++, j++) { |
52eafd66 | 237 | priv->rf[j] = devm_regmap_field_alloc(dev, priv->tm_map, |
c1997054 AK |
238 | priv->fields[j]); |
239 | if (IS_ERR(priv->rf[j])) { | |
240 | ret = PTR_ERR(priv->rf[j]); | |
a245b62b | 241 | goto err_put_device; |
c1997054 AK |
242 | } |
243 | } | |
1b6e3e51 | 244 | for (i = 0, j = VALID_0; i < priv->feat->max_sensors; i++, j++) { |
52eafd66 | 245 | priv->rf[j] = devm_regmap_field_alloc(dev, priv->tm_map, |
c1997054 AK |
246 | priv->fields[j]); |
247 | if (IS_ERR(priv->rf[j])) { | |
248 | ret = PTR_ERR(priv->rf[j]); | |
a245b62b | 249 | goto err_put_device; |
c8c3b091 AK |
250 | } |
251 | } | |
252 | ||
9066073c | 253 | return 0; |
a245b62b PH |
254 | |
255 | err_put_device: | |
256 | put_device(&op->dev); | |
257 | return ret; | |
9066073c | 258 | } |