qcom-pon.c 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. // SPDX-License-Identifier: GPL-2.0
  2. // Copyright (c) 2017-18 Linaro Limited
  3. #include <linux/delay.h>
  4. #include <linux/errno.h>
  5. #include <linux/kernel.h>
  6. #include <linux/module.h>
  7. #include <linux/of.h>
  8. #include <linux/of_platform.h>
  9. #include <linux/platform_device.h>
  10. #include <linux/reboot.h>
  11. #include <linux/reboot-mode.h>
  12. #include <linux/regmap.h>
  13. #define PON_SOFT_RB_SPARE 0x8f
  14. #define GEN1_REASON_SHIFT 2
  15. #define GEN2_REASON_SHIFT 1
  16. #define NO_REASON_SHIFT 0
  17. struct qcom_pon {
  18. struct device *dev;
  19. struct regmap *regmap;
  20. u32 baseaddr;
  21. struct reboot_mode_driver reboot_mode;
  22. long reason_shift;
  23. };
  24. static int qcom_pon_reboot_mode_write(struct reboot_mode_driver *reboot,
  25. unsigned int magic)
  26. {
  27. struct qcom_pon *pon = container_of
  28. (reboot, struct qcom_pon, reboot_mode);
  29. int ret;
  30. ret = regmap_update_bits(pon->regmap,
  31. pon->baseaddr + PON_SOFT_RB_SPARE,
  32. GENMASK(7, pon->reason_shift),
  33. magic << pon->reason_shift);
  34. if (ret < 0)
  35. dev_err(pon->dev, "update reboot mode bits failed\n");
  36. return ret;
  37. }
  38. static int qcom_pon_probe(struct platform_device *pdev)
  39. {
  40. struct qcom_pon *pon;
  41. long reason_shift;
  42. int error;
  43. pon = devm_kzalloc(&pdev->dev, sizeof(*pon), GFP_KERNEL);
  44. if (!pon)
  45. return -ENOMEM;
  46. pon->dev = &pdev->dev;
  47. pon->regmap = dev_get_regmap(pdev->dev.parent, NULL);
  48. if (!pon->regmap) {
  49. dev_err(&pdev->dev, "failed to locate regmap\n");
  50. return -ENODEV;
  51. }
  52. error = of_property_read_u32(pdev->dev.of_node, "reg",
  53. &pon->baseaddr);
  54. if (error)
  55. return error;
  56. reason_shift = (long)of_device_get_match_data(&pdev->dev);
  57. if (reason_shift != NO_REASON_SHIFT) {
  58. pon->reboot_mode.dev = &pdev->dev;
  59. pon->reason_shift = reason_shift;
  60. pon->reboot_mode.write = qcom_pon_reboot_mode_write;
  61. error = devm_reboot_mode_register(&pdev->dev, &pon->reboot_mode);
  62. if (error) {
  63. dev_err(&pdev->dev, "can't register reboot mode\n");
  64. return error;
  65. }
  66. }
  67. platform_set_drvdata(pdev, pon);
  68. return devm_of_platform_populate(&pdev->dev);
  69. }
  70. static const struct of_device_id qcom_pon_id_table[] = {
  71. { .compatible = "qcom,pm8916-pon", .data = (void *)GEN1_REASON_SHIFT },
  72. { .compatible = "qcom,pm8941-pon", .data = (void *)NO_REASON_SHIFT },
  73. { .compatible = "qcom,pms405-pon", .data = (void *)GEN1_REASON_SHIFT },
  74. { .compatible = "qcom,pm8998-pon", .data = (void *)GEN2_REASON_SHIFT },
  75. { .compatible = "qcom,pmk8350-pon", .data = (void *)GEN2_REASON_SHIFT },
  76. { }
  77. };
  78. MODULE_DEVICE_TABLE(of, qcom_pon_id_table);
  79. static struct platform_driver qcom_pon_driver = {
  80. .probe = qcom_pon_probe,
  81. .driver = {
  82. .name = "qcom-pon",
  83. .of_match_table = qcom_pon_id_table,
  84. },
  85. };
  86. module_platform_driver(qcom_pon_driver);
  87. MODULE_DESCRIPTION("Qualcomm Power On driver");
  88. MODULE_LICENSE("GPL v2");