armada_thermal.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. /*
  2. * Marvell Armada 370/XP thermal sensor driver
  3. *
  4. * Copyright (C) 2013 Marvell
  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. */
  16. #include <linux/device.h>
  17. #include <linux/err.h>
  18. #include <linux/io.h>
  19. #include <linux/kernel.h>
  20. #include <linux/of.h>
  21. #include <linux/module.h>
  22. #include <linux/delay.h>
  23. #include <linux/platform_device.h>
  24. #include <linux/of_device.h>
  25. #include <linux/thermal.h>
  26. #define THERMAL_VALID_MASK 0x1
  27. /* Thermal Manager Control and Status Register */
  28. #define PMU_TDC0_SW_RST_MASK (0x1 << 1)
  29. #define PMU_TM_DISABLE_OFFS 0
  30. #define PMU_TM_DISABLE_MASK (0x1 << PMU_TM_DISABLE_OFFS)
  31. #define PMU_TDC0_REF_CAL_CNT_OFFS 11
  32. #define PMU_TDC0_REF_CAL_CNT_MASK (0x1ff << PMU_TDC0_REF_CAL_CNT_OFFS)
  33. #define PMU_TDC0_OTF_CAL_MASK (0x1 << 30)
  34. #define PMU_TDC0_START_CAL_MASK (0x1 << 25)
  35. #define A375_UNIT_CONTROL_SHIFT 27
  36. #define A375_UNIT_CONTROL_MASK 0x7
  37. #define A375_READOUT_INVERT BIT(15)
  38. #define A375_HW_RESETn BIT(8)
  39. #define A380_HW_RESET BIT(8)
  40. struct armada_thermal_data;
  41. /* Marvell EBU Thermal Sensor Dev Structure */
  42. struct armada_thermal_priv {
  43. void __iomem *sensor;
  44. void __iomem *control;
  45. struct armada_thermal_data *data;
  46. };
  47. struct armada_thermal_data {
  48. /* Initialize the sensor */
  49. void (*init_sensor)(struct platform_device *pdev,
  50. struct armada_thermal_priv *);
  51. /* Test for a valid sensor value (optional) */
  52. bool (*is_valid)(struct armada_thermal_priv *);
  53. /* Formula coeficients: temp = (b + m * reg) / div */
  54. unsigned long coef_b;
  55. unsigned long coef_m;
  56. unsigned long coef_div;
  57. bool inverted;
  58. /* Register shift and mask to access the sensor temperature */
  59. unsigned int temp_shift;
  60. unsigned int temp_mask;
  61. unsigned int is_valid_shift;
  62. };
  63. static void armadaxp_init_sensor(struct platform_device *pdev,
  64. struct armada_thermal_priv *priv)
  65. {
  66. unsigned long reg;
  67. reg = readl_relaxed(priv->control);
  68. reg |= PMU_TDC0_OTF_CAL_MASK;
  69. writel(reg, priv->control);
  70. /* Reference calibration value */
  71. reg &= ~PMU_TDC0_REF_CAL_CNT_MASK;
  72. reg |= (0xf1 << PMU_TDC0_REF_CAL_CNT_OFFS);
  73. writel(reg, priv->control);
  74. /* Reset the sensor */
  75. reg = readl_relaxed(priv->control);
  76. writel((reg | PMU_TDC0_SW_RST_MASK), priv->control);
  77. writel(reg, priv->control);
  78. /* Enable the sensor */
  79. reg = readl_relaxed(priv->sensor);
  80. reg &= ~PMU_TM_DISABLE_MASK;
  81. writel(reg, priv->sensor);
  82. }
  83. static void armada370_init_sensor(struct platform_device *pdev,
  84. struct armada_thermal_priv *priv)
  85. {
  86. unsigned long reg;
  87. reg = readl_relaxed(priv->control);
  88. reg |= PMU_TDC0_OTF_CAL_MASK;
  89. writel(reg, priv->control);
  90. /* Reference calibration value */
  91. reg &= ~PMU_TDC0_REF_CAL_CNT_MASK;
  92. reg |= (0xf1 << PMU_TDC0_REF_CAL_CNT_OFFS);
  93. writel(reg, priv->control);
  94. reg &= ~PMU_TDC0_START_CAL_MASK;
  95. writel(reg, priv->control);
  96. mdelay(10);
  97. }
  98. static void armada375_init_sensor(struct platform_device *pdev,
  99. struct armada_thermal_priv *priv)
  100. {
  101. unsigned long reg;
  102. reg = readl(priv->control + 4);
  103. reg &= ~(A375_UNIT_CONTROL_MASK << A375_UNIT_CONTROL_SHIFT);
  104. reg &= ~A375_READOUT_INVERT;
  105. reg &= ~A375_HW_RESETn;
  106. writel(reg, priv->control + 4);
  107. mdelay(20);
  108. reg |= A375_HW_RESETn;
  109. writel(reg, priv->control + 4);
  110. mdelay(50);
  111. }
  112. static void armada380_init_sensor(struct platform_device *pdev,
  113. struct armada_thermal_priv *priv)
  114. {
  115. unsigned long reg = readl_relaxed(priv->control);
  116. /* Reset hardware once */
  117. if (!(reg & A380_HW_RESET)) {
  118. reg |= A380_HW_RESET;
  119. writel(reg, priv->control);
  120. mdelay(10);
  121. }
  122. }
  123. static bool armada_is_valid(struct armada_thermal_priv *priv)
  124. {
  125. unsigned long reg = readl_relaxed(priv->sensor);
  126. return (reg >> priv->data->is_valid_shift) & THERMAL_VALID_MASK;
  127. }
  128. static int armada_get_temp(struct thermal_zone_device *thermal,
  129. int *temp)
  130. {
  131. struct armada_thermal_priv *priv = thermal->devdata;
  132. unsigned long reg;
  133. unsigned long m, b, div;
  134. /* Valid check */
  135. if (priv->data->is_valid && !priv->data->is_valid(priv)) {
  136. dev_err(&thermal->device,
  137. "Temperature sensor reading not valid\n");
  138. return -EIO;
  139. }
  140. reg = readl_relaxed(priv->sensor);
  141. reg = (reg >> priv->data->temp_shift) & priv->data->temp_mask;
  142. /* Get formula coeficients */
  143. b = priv->data->coef_b;
  144. m = priv->data->coef_m;
  145. div = priv->data->coef_div;
  146. if (priv->data->inverted)
  147. *temp = ((m * reg) - b) / div;
  148. else
  149. *temp = (b - (m * reg)) / div;
  150. return 0;
  151. }
  152. static struct thermal_zone_device_ops ops = {
  153. .get_temp = armada_get_temp,
  154. };
  155. static const struct armada_thermal_data armadaxp_data = {
  156. .init_sensor = armadaxp_init_sensor,
  157. .temp_shift = 10,
  158. .temp_mask = 0x1ff,
  159. .coef_b = 3153000000UL,
  160. .coef_m = 10000000UL,
  161. .coef_div = 13825,
  162. };
  163. static const struct armada_thermal_data armada370_data = {
  164. .is_valid = armada_is_valid,
  165. .init_sensor = armada370_init_sensor,
  166. .is_valid_shift = 9,
  167. .temp_shift = 10,
  168. .temp_mask = 0x1ff,
  169. .coef_b = 3153000000UL,
  170. .coef_m = 10000000UL,
  171. .coef_div = 13825,
  172. };
  173. static const struct armada_thermal_data armada375_data = {
  174. .is_valid = armada_is_valid,
  175. .init_sensor = armada375_init_sensor,
  176. .is_valid_shift = 10,
  177. .temp_shift = 0,
  178. .temp_mask = 0x1ff,
  179. .coef_b = 3171900000UL,
  180. .coef_m = 10000000UL,
  181. .coef_div = 13616,
  182. };
  183. static const struct armada_thermal_data armada380_data = {
  184. .is_valid = armada_is_valid,
  185. .init_sensor = armada380_init_sensor,
  186. .is_valid_shift = 10,
  187. .temp_shift = 0,
  188. .temp_mask = 0x3ff,
  189. .coef_b = 1172499100UL,
  190. .coef_m = 2000096UL,
  191. .coef_div = 4201,
  192. .inverted = true,
  193. };
  194. static const struct of_device_id armada_thermal_id_table[] = {
  195. {
  196. .compatible = "marvell,armadaxp-thermal",
  197. .data = &armadaxp_data,
  198. },
  199. {
  200. .compatible = "marvell,armada370-thermal",
  201. .data = &armada370_data,
  202. },
  203. {
  204. .compatible = "marvell,armada375-thermal",
  205. .data = &armada375_data,
  206. },
  207. {
  208. .compatible = "marvell,armada380-thermal",
  209. .data = &armada380_data,
  210. },
  211. {
  212. /* sentinel */
  213. },
  214. };
  215. MODULE_DEVICE_TABLE(of, armada_thermal_id_table);
  216. static int armada_thermal_probe(struct platform_device *pdev)
  217. {
  218. struct thermal_zone_device *thermal;
  219. const struct of_device_id *match;
  220. struct armada_thermal_priv *priv;
  221. struct resource *res;
  222. match = of_match_device(armada_thermal_id_table, &pdev->dev);
  223. if (!match)
  224. return -ENODEV;
  225. priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
  226. if (!priv)
  227. return -ENOMEM;
  228. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  229. priv->sensor = devm_ioremap_resource(&pdev->dev, res);
  230. if (IS_ERR(priv->sensor))
  231. return PTR_ERR(priv->sensor);
  232. res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
  233. priv->control = devm_ioremap_resource(&pdev->dev, res);
  234. if (IS_ERR(priv->control))
  235. return PTR_ERR(priv->control);
  236. priv->data = (struct armada_thermal_data *)match->data;
  237. priv->data->init_sensor(pdev, priv);
  238. thermal = thermal_zone_device_register("armada_thermal", 0, 0,
  239. priv, &ops, NULL, 0, 0);
  240. if (IS_ERR(thermal)) {
  241. dev_err(&pdev->dev,
  242. "Failed to register thermal zone device\n");
  243. return PTR_ERR(thermal);
  244. }
  245. platform_set_drvdata(pdev, thermal);
  246. return 0;
  247. }
  248. static int armada_thermal_exit(struct platform_device *pdev)
  249. {
  250. struct thermal_zone_device *armada_thermal =
  251. platform_get_drvdata(pdev);
  252. thermal_zone_device_unregister(armada_thermal);
  253. return 0;
  254. }
  255. static struct platform_driver armada_thermal_driver = {
  256. .probe = armada_thermal_probe,
  257. .remove = armada_thermal_exit,
  258. .driver = {
  259. .name = "armada_thermal",
  260. .of_match_table = armada_thermal_id_table,
  261. },
  262. };
  263. module_platform_driver(armada_thermal_driver);
  264. MODULE_AUTHOR("Ezequiel Garcia <ezequiel.garcia@free-electrons.com>");
  265. MODULE_DESCRIPTION("Armada 370/XP thermal driver");
  266. MODULE_LICENSE("GPL v2");