scpi-hwmon.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * System Control and Power Interface(SCPI) based hwmon sensor driver
  4. *
  5. * Copyright (C) 2015 ARM Ltd.
  6. * Punit Agrawal <[email protected]>
  7. */
  8. #include <linux/hwmon.h>
  9. #include <linux/module.h>
  10. #include <linux/of_device.h>
  11. #include <linux/platform_device.h>
  12. #include <linux/scpi_protocol.h>
  13. #include <linux/slab.h>
  14. #include <linux/sysfs.h>
  15. #include <linux/thermal.h>
  16. struct sensor_data {
  17. unsigned int scale;
  18. struct scpi_sensor_info info;
  19. struct device_attribute dev_attr_input;
  20. struct device_attribute dev_attr_label;
  21. char input[20];
  22. char label[20];
  23. };
  24. struct scpi_thermal_zone {
  25. int sensor_id;
  26. struct scpi_sensors *scpi_sensors;
  27. };
  28. struct scpi_sensors {
  29. struct scpi_ops *scpi_ops;
  30. struct sensor_data *data;
  31. struct list_head thermal_zones;
  32. struct attribute **attrs;
  33. struct attribute_group group;
  34. const struct attribute_group *groups[2];
  35. };
  36. static const u32 gxbb_scpi_scale[] = {
  37. [TEMPERATURE] = 1, /* (celsius) */
  38. [VOLTAGE] = 1000, /* (millivolts) */
  39. [CURRENT] = 1000, /* (milliamperes) */
  40. [POWER] = 1000000, /* (microwatts) */
  41. [ENERGY] = 1000000, /* (microjoules) */
  42. };
  43. static const u32 scpi_scale[] = {
  44. [TEMPERATURE] = 1000, /* (millicelsius) */
  45. [VOLTAGE] = 1000, /* (millivolts) */
  46. [CURRENT] = 1000, /* (milliamperes) */
  47. [POWER] = 1000000, /* (microwatts) */
  48. [ENERGY] = 1000000, /* (microjoules) */
  49. };
  50. static void scpi_scale_reading(u64 *value, struct sensor_data *sensor)
  51. {
  52. if (scpi_scale[sensor->info.class] != sensor->scale) {
  53. *value *= scpi_scale[sensor->info.class];
  54. do_div(*value, sensor->scale);
  55. }
  56. }
  57. static int scpi_read_temp(struct thermal_zone_device *tz, int *temp)
  58. {
  59. struct scpi_thermal_zone *zone = tz->devdata;
  60. struct scpi_sensors *scpi_sensors = zone->scpi_sensors;
  61. struct scpi_ops *scpi_ops = scpi_sensors->scpi_ops;
  62. struct sensor_data *sensor = &scpi_sensors->data[zone->sensor_id];
  63. u64 value;
  64. int ret;
  65. ret = scpi_ops->sensor_get_value(sensor->info.sensor_id, &value);
  66. if (ret)
  67. return ret;
  68. scpi_scale_reading(&value, sensor);
  69. *temp = value;
  70. return 0;
  71. }
  72. /* hwmon callback functions */
  73. static ssize_t
  74. scpi_show_sensor(struct device *dev, struct device_attribute *attr, char *buf)
  75. {
  76. struct scpi_sensors *scpi_sensors = dev_get_drvdata(dev);
  77. struct scpi_ops *scpi_ops = scpi_sensors->scpi_ops;
  78. struct sensor_data *sensor;
  79. u64 value;
  80. int ret;
  81. sensor = container_of(attr, struct sensor_data, dev_attr_input);
  82. ret = scpi_ops->sensor_get_value(sensor->info.sensor_id, &value);
  83. if (ret)
  84. return ret;
  85. scpi_scale_reading(&value, sensor);
  86. /*
  87. * Temperature sensor values are treated as signed values based on
  88. * observation even though that is not explicitly specified, and
  89. * because an unsigned u64 temperature does not really make practical
  90. * sense especially when the temperature is below zero degrees Celsius.
  91. */
  92. if (sensor->info.class == TEMPERATURE)
  93. return sprintf(buf, "%lld\n", (s64)value);
  94. return sprintf(buf, "%llu\n", value);
  95. }
  96. static ssize_t
  97. scpi_show_label(struct device *dev, struct device_attribute *attr, char *buf)
  98. {
  99. struct sensor_data *sensor;
  100. sensor = container_of(attr, struct sensor_data, dev_attr_label);
  101. return sprintf(buf, "%s\n", sensor->info.name);
  102. }
  103. static const struct thermal_zone_device_ops scpi_sensor_ops = {
  104. .get_temp = scpi_read_temp,
  105. };
  106. static const struct of_device_id scpi_of_match[] = {
  107. {.compatible = "arm,scpi-sensors", .data = &scpi_scale},
  108. {.compatible = "amlogic,meson-gxbb-scpi-sensors", .data = &gxbb_scpi_scale},
  109. {},
  110. };
  111. MODULE_DEVICE_TABLE(of, scpi_of_match);
  112. static int scpi_hwmon_probe(struct platform_device *pdev)
  113. {
  114. u16 nr_sensors, i;
  115. const u32 *scale;
  116. int num_temp = 0, num_volt = 0, num_current = 0, num_power = 0;
  117. int num_energy = 0;
  118. struct scpi_ops *scpi_ops;
  119. struct device *hwdev, *dev = &pdev->dev;
  120. struct scpi_sensors *scpi_sensors;
  121. int idx, ret;
  122. scpi_ops = get_scpi_ops();
  123. if (!scpi_ops)
  124. return -EPROBE_DEFER;
  125. ret = scpi_ops->sensor_get_capability(&nr_sensors);
  126. if (ret)
  127. return ret;
  128. if (!nr_sensors)
  129. return -ENODEV;
  130. scpi_sensors = devm_kzalloc(dev, sizeof(*scpi_sensors), GFP_KERNEL);
  131. if (!scpi_sensors)
  132. return -ENOMEM;
  133. scpi_sensors->data = devm_kcalloc(dev, nr_sensors,
  134. sizeof(*scpi_sensors->data), GFP_KERNEL);
  135. if (!scpi_sensors->data)
  136. return -ENOMEM;
  137. scpi_sensors->attrs = devm_kcalloc(dev, (nr_sensors * 2) + 1,
  138. sizeof(*scpi_sensors->attrs), GFP_KERNEL);
  139. if (!scpi_sensors->attrs)
  140. return -ENOMEM;
  141. scpi_sensors->scpi_ops = scpi_ops;
  142. scale = of_device_get_match_data(&pdev->dev);
  143. if (!scale) {
  144. dev_err(&pdev->dev, "Unable to initialize scpi-hwmon data\n");
  145. return -ENODEV;
  146. }
  147. for (i = 0, idx = 0; i < nr_sensors; i++) {
  148. struct sensor_data *sensor = &scpi_sensors->data[idx];
  149. ret = scpi_ops->sensor_get_info(i, &sensor->info);
  150. if (ret)
  151. return ret;
  152. switch (sensor->info.class) {
  153. case TEMPERATURE:
  154. snprintf(sensor->input, sizeof(sensor->input),
  155. "temp%d_input", num_temp + 1);
  156. snprintf(sensor->label, sizeof(sensor->input),
  157. "temp%d_label", num_temp + 1);
  158. num_temp++;
  159. break;
  160. case VOLTAGE:
  161. snprintf(sensor->input, sizeof(sensor->input),
  162. "in%d_input", num_volt);
  163. snprintf(sensor->label, sizeof(sensor->input),
  164. "in%d_label", num_volt);
  165. num_volt++;
  166. break;
  167. case CURRENT:
  168. snprintf(sensor->input, sizeof(sensor->input),
  169. "curr%d_input", num_current + 1);
  170. snprintf(sensor->label, sizeof(sensor->input),
  171. "curr%d_label", num_current + 1);
  172. num_current++;
  173. break;
  174. case POWER:
  175. snprintf(sensor->input, sizeof(sensor->input),
  176. "power%d_input", num_power + 1);
  177. snprintf(sensor->label, sizeof(sensor->input),
  178. "power%d_label", num_power + 1);
  179. num_power++;
  180. break;
  181. case ENERGY:
  182. snprintf(sensor->input, sizeof(sensor->input),
  183. "energy%d_input", num_energy + 1);
  184. snprintf(sensor->label, sizeof(sensor->input),
  185. "energy%d_label", num_energy + 1);
  186. num_energy++;
  187. break;
  188. default:
  189. continue;
  190. }
  191. sensor->scale = scale[sensor->info.class];
  192. sensor->dev_attr_input.attr.mode = 0444;
  193. sensor->dev_attr_input.show = scpi_show_sensor;
  194. sensor->dev_attr_input.attr.name = sensor->input;
  195. sensor->dev_attr_label.attr.mode = 0444;
  196. sensor->dev_attr_label.show = scpi_show_label;
  197. sensor->dev_attr_label.attr.name = sensor->label;
  198. scpi_sensors->attrs[idx << 1] = &sensor->dev_attr_input.attr;
  199. scpi_sensors->attrs[(idx << 1) + 1] = &sensor->dev_attr_label.attr;
  200. sysfs_attr_init(scpi_sensors->attrs[idx << 1]);
  201. sysfs_attr_init(scpi_sensors->attrs[(idx << 1) + 1]);
  202. idx++;
  203. }
  204. scpi_sensors->group.attrs = scpi_sensors->attrs;
  205. scpi_sensors->groups[0] = &scpi_sensors->group;
  206. platform_set_drvdata(pdev, scpi_sensors);
  207. hwdev = devm_hwmon_device_register_with_groups(dev,
  208. "scpi_sensors", scpi_sensors, scpi_sensors->groups);
  209. if (IS_ERR(hwdev))
  210. return PTR_ERR(hwdev);
  211. /*
  212. * Register the temperature sensors with the thermal framework
  213. * to allow their usage in setting up the thermal zones from
  214. * device tree.
  215. *
  216. * NOTE: Not all temperature sensors maybe used for thermal
  217. * control
  218. */
  219. INIT_LIST_HEAD(&scpi_sensors->thermal_zones);
  220. for (i = 0; i < nr_sensors; i++) {
  221. struct sensor_data *sensor = &scpi_sensors->data[i];
  222. struct thermal_zone_device *z;
  223. struct scpi_thermal_zone *zone;
  224. if (sensor->info.class != TEMPERATURE)
  225. continue;
  226. zone = devm_kzalloc(dev, sizeof(*zone), GFP_KERNEL);
  227. if (!zone)
  228. return -ENOMEM;
  229. zone->sensor_id = i;
  230. zone->scpi_sensors = scpi_sensors;
  231. z = devm_thermal_of_zone_register(dev,
  232. sensor->info.sensor_id,
  233. zone,
  234. &scpi_sensor_ops);
  235. /*
  236. * The call to thermal_zone_of_sensor_register returns
  237. * an error for sensors that are not associated with
  238. * any thermal zones or if the thermal subsystem is
  239. * not configured.
  240. */
  241. if (IS_ERR(z))
  242. devm_kfree(dev, zone);
  243. }
  244. return 0;
  245. }
  246. static struct platform_driver scpi_hwmon_platdrv = {
  247. .driver = {
  248. .name = "scpi-hwmon",
  249. .of_match_table = scpi_of_match,
  250. },
  251. .probe = scpi_hwmon_probe,
  252. };
  253. module_platform_driver(scpi_hwmon_platdrv);
  254. MODULE_AUTHOR("Punit Agrawal <[email protected]>");
  255. MODULE_DESCRIPTION("ARM SCPI HWMON interface driver");
  256. MODULE_LICENSE("GPL v2");