ufs-hwmon.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * UFS hardware monitoring support
  4. * Copyright (c) 2021, Western Digital Corporation
  5. */
  6. #include <linux/hwmon.h>
  7. #include <linux/units.h>
  8. #include <ufs/ufshcd.h>
  9. #include "ufshcd-priv.h"
  10. struct ufs_hwmon_data {
  11. struct ufs_hba *hba;
  12. u8 mask;
  13. };
  14. static int ufs_read_temp_enable(struct ufs_hba *hba, u8 mask, long *val)
  15. {
  16. u32 ee_mask;
  17. int err;
  18. err = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, QUERY_ATTR_IDN_EE_CONTROL, 0, 0,
  19. &ee_mask);
  20. if (err)
  21. return err;
  22. *val = (mask & ee_mask & MASK_EE_TOO_HIGH_TEMP) || (mask & ee_mask & MASK_EE_TOO_LOW_TEMP);
  23. return 0;
  24. }
  25. static int ufs_get_temp(struct ufs_hba *hba, enum attr_idn idn, long *val)
  26. {
  27. u32 value;
  28. int err;
  29. err = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, idn, 0, 0, &value);
  30. if (err)
  31. return err;
  32. if (value == 0)
  33. return -ENODATA;
  34. *val = ((long)value - 80) * MILLIDEGREE_PER_DEGREE;
  35. return 0;
  36. }
  37. static int ufs_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
  38. long *val)
  39. {
  40. struct ufs_hwmon_data *data = dev_get_drvdata(dev);
  41. struct ufs_hba *hba = data->hba;
  42. int err;
  43. down(&hba->host_sem);
  44. if (!ufshcd_is_user_access_allowed(hba)) {
  45. up(&hba->host_sem);
  46. return -EBUSY;
  47. }
  48. ufshcd_rpm_get_sync(hba);
  49. switch (attr) {
  50. case hwmon_temp_enable:
  51. err = ufs_read_temp_enable(hba, data->mask, val);
  52. break;
  53. case hwmon_temp_crit:
  54. err = ufs_get_temp(hba, QUERY_ATTR_IDN_HIGH_TEMP_BOUND, val);
  55. break;
  56. case hwmon_temp_lcrit:
  57. err = ufs_get_temp(hba, QUERY_ATTR_IDN_LOW_TEMP_BOUND, val);
  58. break;
  59. case hwmon_temp_input:
  60. err = ufs_get_temp(hba, QUERY_ATTR_IDN_CASE_ROUGH_TEMP, val);
  61. break;
  62. default:
  63. err = -EOPNOTSUPP;
  64. break;
  65. }
  66. ufshcd_rpm_put_sync(hba);
  67. up(&hba->host_sem);
  68. return err;
  69. }
  70. static int ufs_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
  71. long val)
  72. {
  73. struct ufs_hwmon_data *data = dev_get_drvdata(dev);
  74. struct ufs_hba *hba = data->hba;
  75. int err;
  76. if (attr != hwmon_temp_enable)
  77. return -EINVAL;
  78. if (val != 0 && val != 1)
  79. return -EINVAL;
  80. down(&hba->host_sem);
  81. if (!ufshcd_is_user_access_allowed(hba)) {
  82. up(&hba->host_sem);
  83. return -EBUSY;
  84. }
  85. ufshcd_rpm_get_sync(hba);
  86. if (val == 1)
  87. err = ufshcd_update_ee_usr_mask(hba, MASK_EE_URGENT_TEMP, 0);
  88. else
  89. err = ufshcd_update_ee_usr_mask(hba, 0, MASK_EE_URGENT_TEMP);
  90. ufshcd_rpm_put_sync(hba);
  91. up(&hba->host_sem);
  92. return err;
  93. }
  94. static umode_t ufs_hwmon_is_visible(const void *data,
  95. enum hwmon_sensor_types type, u32 attr,
  96. int channel)
  97. {
  98. if (type != hwmon_temp)
  99. return 0;
  100. switch (attr) {
  101. case hwmon_temp_enable:
  102. return 0644;
  103. case hwmon_temp_crit:
  104. case hwmon_temp_lcrit:
  105. case hwmon_temp_input:
  106. return 0444;
  107. default:
  108. break;
  109. }
  110. return 0;
  111. }
  112. static const struct hwmon_channel_info *const ufs_hwmon_info[] = {
  113. HWMON_CHANNEL_INFO(temp, HWMON_T_ENABLE | HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_LCRIT),
  114. NULL
  115. };
  116. static const struct hwmon_ops ufs_hwmon_ops = {
  117. .is_visible = ufs_hwmon_is_visible,
  118. .read = ufs_hwmon_read,
  119. .write = ufs_hwmon_write,
  120. };
  121. static const struct hwmon_chip_info ufs_hwmon_hba_info = {
  122. .ops = &ufs_hwmon_ops,
  123. .info = ufs_hwmon_info,
  124. };
  125. void ufs_hwmon_probe(struct ufs_hba *hba, u8 mask)
  126. {
  127. struct device *dev = hba->dev;
  128. struct ufs_hwmon_data *data;
  129. struct device *hwmon;
  130. data = kzalloc(sizeof(*data), GFP_KERNEL);
  131. if (!data)
  132. return;
  133. data->hba = hba;
  134. data->mask = mask;
  135. hwmon = hwmon_device_register_with_info(dev, "ufs", data, &ufs_hwmon_hba_info, NULL);
  136. if (IS_ERR(hwmon)) {
  137. dev_warn(dev, "Failed to instantiate hwmon device\n");
  138. kfree(data);
  139. return;
  140. }
  141. hba->hwmon_device = hwmon;
  142. }
  143. void ufs_hwmon_remove(struct ufs_hba *hba)
  144. {
  145. struct ufs_hwmon_data *data;
  146. if (!hba->hwmon_device)
  147. return;
  148. data = dev_get_drvdata(hba->hwmon_device);
  149. hwmon_device_unregister(hba->hwmon_device);
  150. hba->hwmon_device = NULL;
  151. kfree(data);
  152. }
  153. void ufs_hwmon_notify_event(struct ufs_hba *hba, u8 ee_mask)
  154. {
  155. if (!hba->hwmon_device)
  156. return;
  157. if (ee_mask & MASK_EE_TOO_HIGH_TEMP)
  158. hwmon_notify_event(hba->hwmon_device, hwmon_temp, hwmon_temp_max_alarm, 0);
  159. if (ee_mask & MASK_EE_TOO_LOW_TEMP)
  160. hwmon_notify_event(hba->hwmon_device, hwmon_temp, hwmon_temp_min_alarm, 0);
  161. }