khadas_mcu_fan.c 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Khadas MCU Controlled FAN driver
  4. *
  5. * Copyright (C) 2020 BayLibre SAS
  6. * Author(s): Neil Armstrong <narmstrong@baylibre.com>
  7. */
  8. #include <linux/module.h>
  9. #include <linux/of.h>
  10. #include <linux/platform_device.h>
  11. #include <linux/mfd/khadas-mcu.h>
  12. #include <linux/regmap.h>
  13. #include <linux/sysfs.h>
  14. #include <linux/thermal.h>
  15. #define MAX_LEVEL 3
  16. struct khadas_mcu_fan_ctx {
  17. struct khadas_mcu *mcu;
  18. unsigned int level;
  19. struct thermal_cooling_device *cdev;
  20. };
  21. static int khadas_mcu_fan_set_level(struct khadas_mcu_fan_ctx *ctx,
  22. unsigned int level)
  23. {
  24. int ret;
  25. ret = regmap_write(ctx->mcu->regmap, KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG,
  26. level);
  27. if (ret)
  28. return ret;
  29. ctx->level = level;
  30. return 0;
  31. }
  32. static int khadas_mcu_fan_get_max_state(struct thermal_cooling_device *cdev,
  33. unsigned long *state)
  34. {
  35. *state = MAX_LEVEL;
  36. return 0;
  37. }
  38. static int khadas_mcu_fan_get_cur_state(struct thermal_cooling_device *cdev,
  39. unsigned long *state)
  40. {
  41. struct khadas_mcu_fan_ctx *ctx = cdev->devdata;
  42. *state = ctx->level;
  43. return 0;
  44. }
  45. static int
  46. khadas_mcu_fan_set_cur_state(struct thermal_cooling_device *cdev,
  47. unsigned long state)
  48. {
  49. struct khadas_mcu_fan_ctx *ctx = cdev->devdata;
  50. if (state > MAX_LEVEL)
  51. return -EINVAL;
  52. if (state == ctx->level)
  53. return 0;
  54. return khadas_mcu_fan_set_level(ctx, state);
  55. }
  56. static const struct thermal_cooling_device_ops khadas_mcu_fan_cooling_ops = {
  57. .get_max_state = khadas_mcu_fan_get_max_state,
  58. .get_cur_state = khadas_mcu_fan_get_cur_state,
  59. .set_cur_state = khadas_mcu_fan_set_cur_state,
  60. };
  61. static int khadas_mcu_fan_probe(struct platform_device *pdev)
  62. {
  63. struct khadas_mcu *mcu = dev_get_drvdata(pdev->dev.parent);
  64. struct thermal_cooling_device *cdev;
  65. struct device *dev = &pdev->dev;
  66. struct khadas_mcu_fan_ctx *ctx;
  67. int ret;
  68. ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
  69. if (!ctx)
  70. return -ENOMEM;
  71. ctx->mcu = mcu;
  72. platform_set_drvdata(pdev, ctx);
  73. cdev = devm_thermal_of_cooling_device_register(dev->parent,
  74. dev->parent->of_node, "khadas-mcu-fan", ctx,
  75. &khadas_mcu_fan_cooling_ops);
  76. if (IS_ERR(cdev)) {
  77. ret = PTR_ERR(cdev);
  78. dev_err(dev, "Failed to register khadas-mcu-fan as cooling device: %d\n",
  79. ret);
  80. return ret;
  81. }
  82. ctx->cdev = cdev;
  83. thermal_cdev_update(cdev);
  84. return 0;
  85. }
  86. static void khadas_mcu_fan_shutdown(struct platform_device *pdev)
  87. {
  88. struct khadas_mcu_fan_ctx *ctx = platform_get_drvdata(pdev);
  89. khadas_mcu_fan_set_level(ctx, 0);
  90. }
  91. #ifdef CONFIG_PM_SLEEP
  92. static int khadas_mcu_fan_suspend(struct device *dev)
  93. {
  94. struct khadas_mcu_fan_ctx *ctx = dev_get_drvdata(dev);
  95. unsigned int level_save = ctx->level;
  96. int ret;
  97. ret = khadas_mcu_fan_set_level(ctx, 0);
  98. if (ret)
  99. return ret;
  100. ctx->level = level_save;
  101. return 0;
  102. }
  103. static int khadas_mcu_fan_resume(struct device *dev)
  104. {
  105. struct khadas_mcu_fan_ctx *ctx = dev_get_drvdata(dev);
  106. return khadas_mcu_fan_set_level(ctx, ctx->level);
  107. }
  108. #endif
  109. static SIMPLE_DEV_PM_OPS(khadas_mcu_fan_pm, khadas_mcu_fan_suspend,
  110. khadas_mcu_fan_resume);
  111. static const struct platform_device_id khadas_mcu_fan_id_table[] = {
  112. { .name = "khadas-mcu-fan-ctrl", },
  113. {},
  114. };
  115. MODULE_DEVICE_TABLE(platform, khadas_mcu_fan_id_table);
  116. static struct platform_driver khadas_mcu_fan_driver = {
  117. .probe = khadas_mcu_fan_probe,
  118. .shutdown = khadas_mcu_fan_shutdown,
  119. .driver = {
  120. .name = "khadas-mcu-fan-ctrl",
  121. .pm = &khadas_mcu_fan_pm,
  122. },
  123. .id_table = khadas_mcu_fan_id_table,
  124. };
  125. module_platform_driver(khadas_mcu_fan_driver);
  126. MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
  127. MODULE_DESCRIPTION("Khadas MCU FAN driver");
  128. MODULE_LICENSE("GPL");