015-1-thermal-qcom-tsens-Add-a-skeletal-TSENS-drivers.patch 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. From 9066073c6c27994a30187abf3b674770b4088348 Mon Sep 17 00:00:00 2001
  2. From: Rajendra Nayak <rnayak@codeaurora.org>
  3. Date: Thu, 5 May 2016 14:21:39 +0530
  4. Subject: thermal: qcom: tsens: Add a skeletal TSENS drivers
  5. TSENS is Qualcomms' thermal temperature sensor device. It
  6. supports reading temperatures from multiple thermal sensors
  7. present on various QCOM SoCs.
  8. Calibration data is generally read from a non-volatile memory
  9. (eeprom) device.
  10. Add a skeleton driver with all the necessary abstractions so
  11. a variety of qcom device families which support TSENS can
  12. add driver extensions.
  13. Also add the required device tree bindings which can be used
  14. to describe the TSENS device in DT.
  15. Signed-off-by: Rajendra Nayak <rnayak@codeaurora.org>
  16. Reviewed-by: Lina Iyer <lina.iyer@linaro.org>
  17. Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
  18. Signed-off-by: Zhang Rui <rui.zhang@intel.com>
  19. ---
  20. .../devicetree/bindings/thermal/qcom-tsens.txt | 21 +++
  21. drivers/thermal/Kconfig | 5 +
  22. drivers/thermal/Makefile | 1 +
  23. drivers/thermal/qcom/Kconfig | 11 ++
  24. drivers/thermal/qcom/Makefile | 2 +
  25. drivers/thermal/qcom/tsens-common.c | 141 +++++++++++++++
  26. drivers/thermal/qcom/tsens.c | 195 +++++++++++++++++++++
  27. drivers/thermal/qcom/tsens.h | 90 ++++++++++
  28. 8 files changed, 466 insertions(+)
  29. create mode 100644 Documentation/devicetree/bindings/thermal/qcom-tsens.txt
  30. create mode 100644 drivers/thermal/qcom/Kconfig
  31. create mode 100644 drivers/thermal/qcom/Makefile
  32. create mode 100644 drivers/thermal/qcom/tsens-common.c
  33. create mode 100644 drivers/thermal/qcom/tsens.c
  34. create mode 100644 drivers/thermal/qcom/tsens.h
  35. --- /dev/null
  36. +++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.txt
  37. @@ -0,0 +1,21 @@
  38. +* QCOM SoC Temperature Sensor (TSENS)
  39. +
  40. +Required properties:
  41. +- compatible :
  42. + - "qcom,msm8916-tsens" : For 8916 Family of SoCs
  43. + - "qcom,msm8974-tsens" : For 8974 Family of SoCs
  44. + - "qcom,msm8996-tsens" : For 8996 Family of SoCs
  45. +
  46. +- reg: Address range of the thermal registers
  47. +- #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description.
  48. +- Refer to Documentation/devicetree/bindings/nvmem/nvmem.txt to know how to specify
  49. +nvmem cells
  50. +
  51. +Example:
  52. +tsens: thermal-sensor@900000 {
  53. + compatible = "qcom,msm8916-tsens";
  54. + reg = <0x4a8000 0x2000>;
  55. + nvmem-cells = <&tsens_caldata>, <&tsens_calsel>;
  56. + nvmem-cell-names = "caldata", "calsel";
  57. + #thermal-sensor-cells = <1>;
  58. + };
  59. --- a/drivers/thermal/Kconfig
  60. +++ b/drivers/thermal/Kconfig
  61. @@ -404,4 +404,9 @@ config BCM2835_THERMAL
  62. help
  63. Support for thermal sensors on Broadcom bcm2835 SoCs.
  64. +menu "Qualcomm thermal drivers"
  65. +depends on (ARCH_QCOM && OF) || COMPILE_TEST
  66. +source "drivers/thermal/qcom/Kconfig"
  67. +endmenu
  68. +
  69. endif
  70. --- a/drivers/thermal/Makefile
  71. +++ b/drivers/thermal/Makefile
  72. @@ -50,3 +50,4 @@ obj-$(CONFIG_ST_THERMAL) += st/
  73. obj-$(CONFIG_TEGRA_SOCTHERM) += tegra_soctherm.o
  74. obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o
  75. obj-$(CONFIG_BCM2835_THERMAL) += bcm2835_thermal.o
  76. +obj-$(CONFIG_QCOM_TSENS) += qcom/
  77. --- /dev/null
  78. +++ b/drivers/thermal/qcom/Kconfig
  79. @@ -0,0 +1,11 @@
  80. +config QCOM_TSENS
  81. + tristate "Qualcomm TSENS Temperature Alarm"
  82. + depends on THERMAL
  83. + depends on QCOM_QFPROM
  84. + depends on ARCH_QCOM || COMPILE_TEST
  85. + help
  86. + This enables the thermal sysfs driver for the TSENS device. It shows
  87. + up in Sysfs as a thermal zone with multiple trip points. Disabling the
  88. + thermal zone device via the mode file results in disabling the sensor.
  89. + Also able to set threshold temperature for both hot and cold and update
  90. + when a threshold is reached.
  91. --- /dev/null
  92. +++ b/drivers/thermal/qcom/Makefile
  93. @@ -0,0 +1,2 @@
  94. +obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o
  95. +qcom_tsens-y += tsens.o tsens-common.o
  96. --- /dev/null
  97. +++ b/drivers/thermal/qcom/tsens-common.c
  98. @@ -0,0 +1,141 @@
  99. +/*
  100. + * Copyright (c) 2015, The Linux Foundation. All rights reserved.
  101. + *
  102. + * This program is free software; you can redistribute it and/or modify
  103. + * it under the terms of the GNU General Public License version 2 and
  104. + * only version 2 as published by the Free Software Foundation.
  105. + *
  106. + * This program is distributed in the hope that it will be useful,
  107. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  108. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  109. + * GNU General Public License for more details.
  110. + *
  111. + */
  112. +
  113. +#include <linux/err.h>
  114. +#include <linux/io.h>
  115. +#include <linux/nvmem-consumer.h>
  116. +#include <linux/of_address.h>
  117. +#include <linux/platform_device.h>
  118. +#include <linux/regmap.h>
  119. +#include "tsens.h"
  120. +
  121. +#define S0_ST_ADDR 0x1030
  122. +#define SN_ADDR_OFFSET 0x4
  123. +#define SN_ST_TEMP_MASK 0x3ff
  124. +#define CAL_DEGC_PT1 30
  125. +#define CAL_DEGC_PT2 120
  126. +#define SLOPE_FACTOR 1000
  127. +#define SLOPE_DEFAULT 3200
  128. +
  129. +char *qfprom_read(struct device *dev, const char *cname)
  130. +{
  131. + struct nvmem_cell *cell;
  132. + ssize_t data;
  133. + char *ret;
  134. +
  135. + cell = nvmem_cell_get(dev, cname);
  136. + if (IS_ERR(cell))
  137. + return ERR_CAST(cell);
  138. +
  139. + ret = nvmem_cell_read(cell, &data);
  140. + nvmem_cell_put(cell);
  141. +
  142. + return ret;
  143. +}
  144. +
  145. +/*
  146. + * Use this function on devices where slope and offset calculations
  147. + * depend on calibration data read from qfprom. On others the slope
  148. + * and offset values are derived from tz->tzp->slope and tz->tzp->offset
  149. + * resp.
  150. + */
  151. +void compute_intercept_slope(struct tsens_device *tmdev, u32 *p1,
  152. + u32 *p2, u32 mode)
  153. +{
  154. + int i;
  155. + int num, den;
  156. +
  157. + for (i = 0; i < tmdev->num_sensors; i++) {
  158. + dev_dbg(tmdev->dev,
  159. + "sensor%d - data_point1:%#x data_point2:%#x\n",
  160. + i, p1[i], p2[i]);
  161. +
  162. + tmdev->sensor[i].slope = SLOPE_DEFAULT;
  163. + if (mode == TWO_PT_CALIB) {
  164. + /*
  165. + * slope (m) = adc_code2 - adc_code1 (y2 - y1)/
  166. + * temp_120_degc - temp_30_degc (x2 - x1)
  167. + */
  168. + num = p2[i] - p1[i];
  169. + num *= SLOPE_FACTOR;
  170. + den = CAL_DEGC_PT2 - CAL_DEGC_PT1;
  171. + tmdev->sensor[i].slope = num / den;
  172. + }
  173. +
  174. + tmdev->sensor[i].offset = (p1[i] * SLOPE_FACTOR) -
  175. + (CAL_DEGC_PT1 *
  176. + tmdev->sensor[i].slope);
  177. + dev_dbg(tmdev->dev, "offset:%d\n", tmdev->sensor[i].offset);
  178. + }
  179. +}
  180. +
  181. +static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s)
  182. +{
  183. + int degc, num, den;
  184. +
  185. + num = (adc_code * SLOPE_FACTOR) - s->offset;
  186. + den = s->slope;
  187. +
  188. + if (num > 0)
  189. + degc = num + (den / 2);
  190. + else if (num < 0)
  191. + degc = num - (den / 2);
  192. + else
  193. + degc = num;
  194. +
  195. + degc /= den;
  196. +
  197. + return degc;
  198. +}
  199. +
  200. +int get_temp_common(struct tsens_device *tmdev, int id, int *temp)
  201. +{
  202. + struct tsens_sensor *s = &tmdev->sensor[id];
  203. + u32 code;
  204. + unsigned int sensor_addr;
  205. + int last_temp = 0, ret;
  206. +
  207. + sensor_addr = S0_ST_ADDR + s->hw_id * SN_ADDR_OFFSET;
  208. + ret = regmap_read(tmdev->map, sensor_addr, &code);
  209. + if (ret)
  210. + return ret;
  211. + last_temp = code & SN_ST_TEMP_MASK;
  212. +
  213. + *temp = code_to_degc(last_temp, s) * 1000;
  214. +
  215. + return 0;
  216. +}
  217. +
  218. +static const struct regmap_config tsens_config = {
  219. + .reg_bits = 32,
  220. + .val_bits = 32,
  221. + .reg_stride = 4,
  222. +};
  223. +
  224. +int __init init_common(struct tsens_device *tmdev)
  225. +{
  226. + void __iomem *base;
  227. +
  228. + base = of_iomap(tmdev->dev->of_node, 0);
  229. + if (IS_ERR(base))
  230. + return -EINVAL;
  231. +
  232. + tmdev->map = devm_regmap_init_mmio(tmdev->dev, base, &tsens_config);
  233. + if (!tmdev->map) {
  234. + iounmap(base);
  235. + return -ENODEV;
  236. + }
  237. +
  238. + return 0;
  239. +}
  240. --- /dev/null
  241. +++ b/drivers/thermal/qcom/tsens.c
  242. @@ -0,0 +1,195 @@
  243. +/*
  244. + * Copyright (c) 2015, The Linux Foundation. All rights reserved.
  245. + *
  246. + * This program is free software; you can redistribute it and/or modify
  247. + * it under the terms of the GNU General Public License version 2 and
  248. + * only version 2 as published by the Free Software Foundation.
  249. + *
  250. + * This program is distributed in the hope that it will be useful,
  251. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  252. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  253. + * GNU General Public License for more details.
  254. + *
  255. + */
  256. +
  257. +#include <linux/err.h>
  258. +#include <linux/module.h>
  259. +#include <linux/of.h>
  260. +#include <linux/platform_device.h>
  261. +#include <linux/pm.h>
  262. +#include <linux/slab.h>
  263. +#include <linux/thermal.h>
  264. +#include "tsens.h"
  265. +
  266. +static int tsens_get_temp(void *data, int *temp)
  267. +{
  268. + const struct tsens_sensor *s = data;
  269. + struct tsens_device *tmdev = s->tmdev;
  270. +
  271. + return tmdev->ops->get_temp(tmdev, s->id, temp);
  272. +}
  273. +
  274. +static int tsens_get_trend(void *data, long *temp)
  275. +{
  276. + const struct tsens_sensor *s = data;
  277. + struct tsens_device *tmdev = s->tmdev;
  278. +
  279. + if (tmdev->ops->get_trend)
  280. + return tmdev->ops->get_trend(tmdev, s->id, temp);
  281. +
  282. + return -ENOTSUPP;
  283. +}
  284. +
  285. +static int tsens_suspend(struct device *dev)
  286. +{
  287. + struct tsens_device *tmdev = dev_get_drvdata(dev);
  288. +
  289. + if (tmdev->ops && tmdev->ops->suspend)
  290. + return tmdev->ops->suspend(tmdev);
  291. +
  292. + return 0;
  293. +}
  294. +
  295. +static int tsens_resume(struct device *dev)
  296. +{
  297. + struct tsens_device *tmdev = dev_get_drvdata(dev);
  298. +
  299. + if (tmdev->ops && tmdev->ops->resume)
  300. + return tmdev->ops->resume(tmdev);
  301. +
  302. + return 0;
  303. +}
  304. +
  305. +static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume);
  306. +
  307. +static const struct of_device_id tsens_table[] = {
  308. + {
  309. + .compatible = "qcom,msm8916-tsens",
  310. + }, {
  311. + .compatible = "qcom,msm8974-tsens",
  312. + },
  313. + {}
  314. +};
  315. +MODULE_DEVICE_TABLE(of, tsens_table);
  316. +
  317. +static const struct thermal_zone_of_device_ops tsens_of_ops = {
  318. + .get_temp = tsens_get_temp,
  319. + .get_trend = tsens_get_trend,
  320. +};
  321. +
  322. +static int tsens_register(struct tsens_device *tmdev)
  323. +{
  324. + int i;
  325. + struct thermal_zone_device *tzd;
  326. + u32 *hw_id, n = tmdev->num_sensors;
  327. +
  328. + hw_id = devm_kcalloc(tmdev->dev, n, sizeof(u32), GFP_KERNEL);
  329. + if (!hw_id)
  330. + return -ENOMEM;
  331. +
  332. + for (i = 0; i < tmdev->num_sensors; i++) {
  333. + tmdev->sensor[i].tmdev = tmdev;
  334. + tmdev->sensor[i].id = i;
  335. + tzd = devm_thermal_zone_of_sensor_register(tmdev->dev, i,
  336. + &tmdev->sensor[i],
  337. + &tsens_of_ops);
  338. + if (IS_ERR(tzd))
  339. + continue;
  340. + tmdev->sensor[i].tzd = tzd;
  341. + if (tmdev->ops->enable)
  342. + tmdev->ops->enable(tmdev, i);
  343. + }
  344. + return 0;
  345. +}
  346. +
  347. +static int tsens_probe(struct platform_device *pdev)
  348. +{
  349. + int ret, i;
  350. + struct device *dev;
  351. + struct device_node *np;
  352. + struct tsens_sensor *s;
  353. + struct tsens_device *tmdev;
  354. + const struct tsens_data *data;
  355. + const struct of_device_id *id;
  356. +
  357. + if (pdev->dev.of_node)
  358. + dev = &pdev->dev;
  359. + else
  360. + dev = pdev->dev.parent;
  361. +
  362. + np = dev->of_node;
  363. +
  364. + id = of_match_node(tsens_table, np);
  365. + if (!id)
  366. + return -EINVAL;
  367. +
  368. + data = id->data;
  369. +
  370. + if (data->num_sensors <= 0) {
  371. + dev_err(dev, "invalid number of sensors\n");
  372. + return -EINVAL;
  373. + }
  374. +
  375. + tmdev = devm_kzalloc(dev, sizeof(*tmdev) +
  376. + data->num_sensors * sizeof(*s), GFP_KERNEL);
  377. + if (!tmdev)
  378. + return -ENOMEM;
  379. +
  380. + tmdev->dev = dev;
  381. + tmdev->num_sensors = data->num_sensors;
  382. + tmdev->ops = data->ops;
  383. + for (i = 0; i < tmdev->num_sensors; i++) {
  384. + if (data->hw_ids)
  385. + tmdev->sensor[i].hw_id = data->hw_ids[i];
  386. + else
  387. + tmdev->sensor[i].hw_id = i;
  388. + }
  389. +
  390. + if (!tmdev->ops || !tmdev->ops->init || !tmdev->ops->get_temp)
  391. + return -EINVAL;
  392. +
  393. + ret = tmdev->ops->init(tmdev);
  394. + if (ret < 0) {
  395. + dev_err(dev, "tsens init failed\n");
  396. + return ret;
  397. + }
  398. +
  399. + if (tmdev->ops->calibrate) {
  400. + ret = tmdev->ops->calibrate(tmdev);
  401. + if (ret < 0) {
  402. + dev_err(dev, "tsens calibration failed\n");
  403. + return ret;
  404. + }
  405. + }
  406. +
  407. + ret = tsens_register(tmdev);
  408. +
  409. + platform_set_drvdata(pdev, tmdev);
  410. +
  411. + return ret;
  412. +}
  413. +
  414. +static int tsens_remove(struct platform_device *pdev)
  415. +{
  416. + struct tsens_device *tmdev = platform_get_drvdata(pdev);
  417. +
  418. + if (tmdev->ops->disable)
  419. + tmdev->ops->disable(tmdev);
  420. +
  421. + return 0;
  422. +}
  423. +
  424. +static struct platform_driver tsens_driver = {
  425. + .probe = tsens_probe,
  426. + .remove = tsens_remove,
  427. + .driver = {
  428. + .name = "qcom-tsens",
  429. + .pm = &tsens_pm_ops,
  430. + .of_match_table = tsens_table,
  431. + },
  432. +};
  433. +module_platform_driver(tsens_driver);
  434. +
  435. +MODULE_LICENSE("GPL v2");
  436. +MODULE_DESCRIPTION("QCOM Temperature Sensor driver");
  437. +MODULE_ALIAS("platform:qcom-tsens");
  438. --- /dev/null
  439. +++ b/drivers/thermal/qcom/tsens.h
  440. @@ -0,0 +1,90 @@
  441. +/*
  442. + * Copyright (c) 2015, The Linux Foundation. All rights reserved.
  443. + *
  444. + * This software is licensed under the terms of the GNU General Public
  445. + * License version 2, as published by the Free Software Foundation, and
  446. + * may be copied, distributed, and modified under those terms.
  447. + *
  448. + * This program is distributed in the hope that it will be useful,
  449. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  450. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  451. + * GNU General Public License for more details.
  452. + */
  453. +#ifndef __QCOM_TSENS_H__
  454. +#define __QCOM_TSENS_H__
  455. +
  456. +#define ONE_PT_CALIB 0x1
  457. +#define ONE_PT_CALIB2 0x2
  458. +#define TWO_PT_CALIB 0x3
  459. +
  460. +struct tsens_device;
  461. +
  462. +struct tsens_sensor {
  463. + struct tsens_device *tmdev;
  464. + struct thermal_zone_device *tzd;
  465. + int offset;
  466. + int id;
  467. + int hw_id;
  468. + int slope;
  469. + u32 status;
  470. +};
  471. +
  472. +/**
  473. + * struct tsens_ops - operations as supported by the tsens device
  474. + * @init: Function to initialize the tsens device
  475. + * @calibrate: Function to calibrate the tsens device
  476. + * @get_temp: Function which returns the temp in millidegC
  477. + * @enable: Function to enable (clocks/power) tsens device
  478. + * @disable: Function to disable the tsens device
  479. + * @suspend: Function to suspend the tsens device
  480. + * @resume: Function to resume the tsens device
  481. + * @get_trend: Function to get the thermal/temp trend
  482. + */
  483. +struct tsens_ops {
  484. + /* mandatory callbacks */
  485. + int (*init)(struct tsens_device *);
  486. + int (*calibrate)(struct tsens_device *);
  487. + int (*get_temp)(struct tsens_device *, int, int *);
  488. + /* optional callbacks */
  489. + int (*enable)(struct tsens_device *, int);
  490. + void (*disable)(struct tsens_device *);
  491. + int (*suspend)(struct tsens_device *);
  492. + int (*resume)(struct tsens_device *);
  493. + int (*get_trend)(struct tsens_device *, int, long *);
  494. +};
  495. +
  496. +/**
  497. + * struct tsens_data - tsens instance specific data
  498. + * @num_sensors: Max number of sensors supported by platform
  499. + * @ops: operations the tsens instance supports
  500. + * @hw_ids: Subset of sensors ids supported by platform, if not the first n
  501. + */
  502. +struct tsens_data {
  503. + const u32 num_sensors;
  504. + const struct tsens_ops *ops;
  505. + unsigned int *hw_ids;
  506. +};
  507. +
  508. +/* Registers to be saved/restored across a context loss */
  509. +struct tsens_context {
  510. + int threshold;
  511. + int control;
  512. +};
  513. +
  514. +struct tsens_device {
  515. + struct device *dev;
  516. + u32 num_sensors;
  517. + struct regmap *map;
  518. + struct regmap_field *status_field;
  519. + struct tsens_context ctx;
  520. + bool trdy;
  521. + const struct tsens_ops *ops;
  522. + struct tsens_sensor sensor[0];
  523. +};
  524. +
  525. +char *qfprom_read(struct device *, const char *);
  526. +void compute_intercept_slope(struct tsens_device *, u32 *, u32 *, u32);
  527. +int init_common(struct tsens_device *);
  528. +int get_temp_common(struct tsens_device *, int, int *);
  529. +
  530. +#endif /* __QCOM_TSENS_H__ */