diff options
author | Csaba Kertesz <csaba.kertesz@vincit.fi> | 2016-10-25 16:08:07 -0400 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2017-01-06 15:35:17 -0500 |
commit | 54643a83b41a2bff90a0615799686b37a1109404 (patch) | |
tree | b6baf81bd6d8abe9270672c90b6906da6c026382 | |
parent | de2518201e526f4491e5856b9818f5c18684a6a7 (diff) |
ahci: imx: Add imx53 SATA temperature sensor support
Add a hwmon entry to get the temperature from the die of imx53
SATA.
The original patch was made by Richard Zhu for kernel 2.6.x:
ENGR00134041-MX53-Add-the-SATA-AHCI-temperature-monitor.patch
Signed-off-by: Fabien Lahoudere <fabien.lahoudere@collabora.co.uk>
Signed-off-by: Tejun Heo <tj@kernel.org>
-rw-r--r-- | drivers/ata/ahci_imx.c | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c index 3f3a7db208ae..420f065978dc 100644 --- a/drivers/ata/ahci_imx.c +++ b/drivers/ata/ahci_imx.c | |||
@@ -26,6 +26,9 @@ | |||
26 | #include <linux/mfd/syscon.h> | 26 | #include <linux/mfd/syscon.h> |
27 | #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> | 27 | #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> |
28 | #include <linux/libata.h> | 28 | #include <linux/libata.h> |
29 | #include <linux/hwmon.h> | ||
30 | #include <linux/hwmon-sysfs.h> | ||
31 | #include <linux/thermal.h> | ||
29 | #include "ahci.h" | 32 | #include "ahci.h" |
30 | 33 | ||
31 | #define DRV_NAME "ahci-imx" | 34 | #define DRV_NAME "ahci-imx" |
@@ -214,6 +217,180 @@ static int imx_sata_phy_reset(struct ahci_host_priv *hpriv) | |||
214 | return timeout ? 0 : -ETIMEDOUT; | 217 | return timeout ? 0 : -ETIMEDOUT; |
215 | } | 218 | } |
216 | 219 | ||
220 | enum { | ||
221 | /* SATA PHY Register */ | ||
222 | SATA_PHY_CR_CLOCK_CRCMP_LT_LIMIT = 0x0001, | ||
223 | SATA_PHY_CR_CLOCK_DAC_CTL = 0x0008, | ||
224 | SATA_PHY_CR_CLOCK_RTUNE_CTL = 0x0009, | ||
225 | SATA_PHY_CR_CLOCK_ADC_OUT = 0x000A, | ||
226 | SATA_PHY_CR_CLOCK_MPLL_TST = 0x0017, | ||
227 | }; | ||
228 | |||
229 | static int read_adc_sum(void *dev, u16 rtune_ctl_reg, void __iomem * mmio) | ||
230 | { | ||
231 | u16 adc_out_reg, read_sum; | ||
232 | u32 index, read_attempt; | ||
233 | const u32 attempt_limit = 100; | ||
234 | |||
235 | imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_RTUNE_CTL, mmio); | ||
236 | imx_phy_reg_write(rtune_ctl_reg, mmio); | ||
237 | |||
238 | /* two dummy read */ | ||
239 | index = 0; | ||
240 | read_attempt = 0; | ||
241 | adc_out_reg = 0; | ||
242 | imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_ADC_OUT, mmio); | ||
243 | while (index < 2) { | ||
244 | imx_phy_reg_read(&adc_out_reg, mmio); | ||
245 | /* check if valid */ | ||
246 | if (adc_out_reg & 0x400) | ||
247 | index++; | ||
248 | |||
249 | read_attempt++; | ||
250 | if (read_attempt > attempt_limit) { | ||
251 | dev_err(dev, "Read REG more than %d times!\n", | ||
252 | attempt_limit); | ||
253 | break; | ||
254 | } | ||
255 | } | ||
256 | |||
257 | index = 0; | ||
258 | read_attempt = 0; | ||
259 | read_sum = 0; | ||
260 | while (index < 80) { | ||
261 | imx_phy_reg_read(&adc_out_reg, mmio); | ||
262 | if (adc_out_reg & 0x400) { | ||
263 | read_sum = read_sum + (adc_out_reg & 0x3FF); | ||
264 | index++; | ||
265 | } | ||
266 | read_attempt++; | ||
267 | if (read_attempt > attempt_limit) { | ||
268 | dev_err(dev, "Read REG more than %d times!\n", | ||
269 | attempt_limit); | ||
270 | break; | ||
271 | } | ||
272 | } | ||
273 | |||
274 | /* Use the U32 to make 1000 precision */ | ||
275 | return (read_sum * 1000) / 80; | ||
276 | } | ||
277 | |||
278 | /* SATA AHCI temperature monitor */ | ||
279 | static int sata_ahci_read_temperature(void *dev, int *temp) | ||
280 | { | ||
281 | u16 mpll_test_reg, rtune_ctl_reg, dac_ctl_reg, read_sum; | ||
282 | u32 str1, str2, str3, str4; | ||
283 | int m1, m2, a; | ||
284 | struct ahci_host_priv *hpriv = dev_get_drvdata(dev); | ||
285 | void __iomem *mmio = hpriv->mmio; | ||
286 | |||
287 | /* check rd-wr to reg */ | ||
288 | read_sum = 0; | ||
289 | imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_CRCMP_LT_LIMIT, mmio); | ||
290 | imx_phy_reg_write(read_sum, mmio); | ||
291 | imx_phy_reg_read(&read_sum, mmio); | ||
292 | if ((read_sum & 0xffff) != 0) | ||
293 | dev_err(dev, "Read/Write REG error, 0x%x!\n", read_sum); | ||
294 | |||
295 | imx_phy_reg_write(0x5A5A, mmio); | ||
296 | imx_phy_reg_read(&read_sum, mmio); | ||
297 | if ((read_sum & 0xffff) != 0x5A5A) | ||
298 | dev_err(dev, "Read/Write REG error, 0x%x!\n", read_sum); | ||
299 | |||
300 | imx_phy_reg_write(0x1234, mmio); | ||
301 | imx_phy_reg_read(&read_sum, mmio); | ||
302 | if ((read_sum & 0xffff) != 0x1234) | ||
303 | dev_err(dev, "Read/Write REG error, 0x%x!\n", read_sum); | ||
304 | |||
305 | /* start temperature test */ | ||
306 | imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_MPLL_TST, mmio); | ||
307 | imx_phy_reg_read(&mpll_test_reg, mmio); | ||
308 | imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_RTUNE_CTL, mmio); | ||
309 | imx_phy_reg_read(&rtune_ctl_reg, mmio); | ||
310 | imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_DAC_CTL, mmio); | ||
311 | imx_phy_reg_read(&dac_ctl_reg, mmio); | ||
312 | |||
313 | /* mpll_tst.meas_iv ([12:2]) */ | ||
314 | str1 = (mpll_test_reg >> 2) & 0x7FF; | ||
315 | /* rtune_ctl.mode ([1:0]) */ | ||
316 | str2 = (rtune_ctl_reg) & 0x3; | ||
317 | /* dac_ctl.dac_mode ([14:12]) */ | ||
318 | str3 = (dac_ctl_reg >> 12) & 0x7; | ||
319 | /* rtune_ctl.sel_atbp ([4]) */ | ||
320 | str4 = (rtune_ctl_reg >> 4); | ||
321 | |||
322 | /* Calculate the m1 */ | ||
323 | /* mpll_tst.meas_iv */ | ||
324 | mpll_test_reg = (mpll_test_reg & 0xE03) | (512) << 2; | ||
325 | /* rtune_ctl.mode */ | ||
326 | rtune_ctl_reg = (rtune_ctl_reg & 0xFFC) | (1); | ||
327 | /* dac_ctl.dac_mode */ | ||
328 | dac_ctl_reg = (dac_ctl_reg & 0x8FF) | (4) << 12; | ||
329 | /* rtune_ctl.sel_atbp */ | ||
330 | rtune_ctl_reg = (rtune_ctl_reg & 0xFEF) | (0) << 4; | ||
331 | imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_MPLL_TST, mmio); | ||
332 | imx_phy_reg_write(mpll_test_reg, mmio); | ||
333 | imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_DAC_CTL, mmio); | ||
334 | imx_phy_reg_write(dac_ctl_reg, mmio); | ||
335 | m1 = read_adc_sum(dev, rtune_ctl_reg, mmio); | ||
336 | |||
337 | /* Calculate the m2 */ | ||
338 | /* rtune_ctl.sel_atbp */ | ||
339 | rtune_ctl_reg = (rtune_ctl_reg & 0xFEF) | (1) << 4; | ||
340 | m2 = read_adc_sum(dev, rtune_ctl_reg, mmio); | ||
341 | |||
342 | /* restore the status */ | ||
343 | /* mpll_tst.meas_iv */ | ||
344 | mpll_test_reg = (mpll_test_reg & 0xE03) | (str1) << 2; | ||
345 | /* rtune_ctl.mode */ | ||
346 | rtune_ctl_reg = (rtune_ctl_reg & 0xFFC) | (str2); | ||
347 | /* dac_ctl.dac_mode */ | ||
348 | dac_ctl_reg = (dac_ctl_reg & 0x8FF) | (str3) << 12; | ||
349 | /* rtune_ctl.sel_atbp */ | ||
350 | rtune_ctl_reg = (rtune_ctl_reg & 0xFEF) | (str4) << 4; | ||
351 | |||
352 | imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_MPLL_TST, mmio); | ||
353 | imx_phy_reg_write(mpll_test_reg, mmio); | ||
354 | imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_DAC_CTL, mmio); | ||
355 | imx_phy_reg_write(dac_ctl_reg, mmio); | ||
356 | imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_RTUNE_CTL, mmio); | ||
357 | imx_phy_reg_write(rtune_ctl_reg, mmio); | ||
358 | |||
359 | /* Compute temperature */ | ||
360 | if (!(m2 / 1000)) | ||
361 | m2 = 1000; | ||
362 | a = (m2 - m1) / (m2/1000); | ||
363 | *temp = ((-559) * a * a) / 1000 + (1379) * a + (-458000); | ||
364 | |||
365 | return 0; | ||
366 | } | ||
367 | |||
368 | static ssize_t sata_ahci_show_temp(struct device *dev, | ||
369 | struct device_attribute *da, | ||
370 | char *buf) | ||
371 | { | ||
372 | unsigned int temp = 0; | ||
373 | int err; | ||
374 | |||
375 | err = sata_ahci_read_temperature(dev, &temp); | ||
376 | if (err < 0) | ||
377 | return err; | ||
378 | |||
379 | return sprintf(buf, "%u\n", temp); | ||
380 | } | ||
381 | |||
382 | static const struct thermal_zone_of_device_ops fsl_sata_ahci_of_thermal_ops = { | ||
383 | .get_temp = sata_ahci_read_temperature, | ||
384 | }; | ||
385 | |||
386 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, sata_ahci_show_temp, NULL, 0); | ||
387 | |||
388 | static struct attribute *fsl_sata_ahci_attrs[] = { | ||
389 | &sensor_dev_attr_temp1_input.dev_attr.attr, | ||
390 | NULL | ||
391 | }; | ||
392 | ATTRIBUTE_GROUPS(fsl_sata_ahci); | ||
393 | |||
217 | static int imx_sata_enable(struct ahci_host_priv *hpriv) | 394 | static int imx_sata_enable(struct ahci_host_priv *hpriv) |
218 | { | 395 | { |
219 | struct imx_ahci_priv *imxpriv = hpriv->plat_data; | 396 | struct imx_ahci_priv *imxpriv = hpriv->plat_data; |
@@ -597,6 +774,24 @@ static int imx_ahci_probe(struct platform_device *pdev) | |||
597 | if (ret) | 774 | if (ret) |
598 | return ret; | 775 | return ret; |
599 | 776 | ||
777 | if (imxpriv->type == AHCI_IMX53) { | ||
778 | /* Add the temperature monitor */ | ||
779 | struct device *hwmon_dev; | ||
780 | |||
781 | hwmon_dev = | ||
782 | devm_hwmon_device_register_with_groups(dev, | ||
783 | "sata_ahci", | ||
784 | hpriv, | ||
785 | fsl_sata_ahci_groups); | ||
786 | if (IS_ERR(hwmon_dev)) { | ||
787 | ret = PTR_ERR(hwmon_dev); | ||
788 | goto disable_clk; | ||
789 | } | ||
790 | devm_thermal_zone_of_sensor_register(hwmon_dev, 0, hwmon_dev, | ||
791 | &fsl_sata_ahci_of_thermal_ops); | ||
792 | dev_info(dev, "%s: sensor 'sata_ahci'\n", dev_name(hwmon_dev)); | ||
793 | } | ||
794 | |||
600 | ret = imx_sata_enable(hpriv); | 795 | ret = imx_sata_enable(hpriv); |
601 | if (ret) | 796 | if (ret) |
602 | goto disable_clk; | 797 | goto disable_clk; |