qcom_hwspinlock.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (c) 2013, The Linux Foundation. All rights reserved.
  4. * Copyright (c) 2015, Sony Mobile Communications AB
  5. */
  6. #include <linux/hwspinlock.h>
  7. #include <linux/io.h>
  8. #include <linux/kernel.h>
  9. #include <linux/mfd/syscon.h>
  10. #include <linux/module.h>
  11. #include <linux/of.h>
  12. #include <linux/of_device.h>
  13. #include <linux/platform_device.h>
  14. #include <linux/regmap.h>
  15. #include "hwspinlock_internal.h"
  16. #define QCOM_MUTEX_APPS_PROC_ID 1
  17. #define QCOM_MUTEX_NUM_LOCKS 32
  18. static int qcom_hwspinlock_trylock(struct hwspinlock *lock)
  19. {
  20. struct regmap_field *field = lock->priv;
  21. u32 lock_owner;
  22. int ret;
  23. ret = regmap_field_write(field, QCOM_MUTEX_APPS_PROC_ID);
  24. if (ret)
  25. return ret;
  26. ret = regmap_field_read(field, &lock_owner);
  27. if (ret)
  28. return ret;
  29. return lock_owner == QCOM_MUTEX_APPS_PROC_ID;
  30. }
  31. static void qcom_hwspinlock_unlock(struct hwspinlock *lock)
  32. {
  33. struct regmap_field *field = lock->priv;
  34. u32 lock_owner;
  35. int ret;
  36. ret = regmap_field_read(field, &lock_owner);
  37. if (ret) {
  38. pr_err("%s: unable to query spinlock owner\n", __func__);
  39. return;
  40. }
  41. if (lock_owner != QCOM_MUTEX_APPS_PROC_ID) {
  42. pr_err("%s: spinlock not owned by us (actual owner is %d)\n",
  43. __func__, lock_owner);
  44. }
  45. ret = regmap_field_write(field, 0);
  46. if (ret)
  47. pr_err("%s: failed to unlock spinlock\n", __func__);
  48. }
  49. static const struct hwspinlock_ops qcom_hwspinlock_ops = {
  50. .trylock = qcom_hwspinlock_trylock,
  51. .unlock = qcom_hwspinlock_unlock,
  52. };
  53. static const struct of_device_id qcom_hwspinlock_of_match[] = {
  54. { .compatible = "qcom,sfpb-mutex" },
  55. { .compatible = "qcom,tcsr-mutex" },
  56. { }
  57. };
  58. MODULE_DEVICE_TABLE(of, qcom_hwspinlock_of_match);
  59. static struct regmap *qcom_hwspinlock_probe_syscon(struct platform_device *pdev,
  60. u32 *base, u32 *stride)
  61. {
  62. struct device_node *syscon;
  63. struct regmap *regmap;
  64. int ret;
  65. syscon = of_parse_phandle(pdev->dev.of_node, "syscon", 0);
  66. if (!syscon)
  67. return ERR_PTR(-ENODEV);
  68. regmap = syscon_node_to_regmap(syscon);
  69. of_node_put(syscon);
  70. if (IS_ERR(regmap))
  71. return regmap;
  72. ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, base);
  73. if (ret < 0) {
  74. dev_err(&pdev->dev, "no offset in syscon\n");
  75. return ERR_PTR(-EINVAL);
  76. }
  77. ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 2, stride);
  78. if (ret < 0) {
  79. dev_err(&pdev->dev, "no stride syscon\n");
  80. return ERR_PTR(-EINVAL);
  81. }
  82. return regmap;
  83. }
  84. static const struct regmap_config tcsr_mutex_config = {
  85. .reg_bits = 32,
  86. .reg_stride = 4,
  87. .val_bits = 32,
  88. .max_register = 0x40000,
  89. .fast_io = true,
  90. };
  91. static struct regmap *qcom_hwspinlock_probe_mmio(struct platform_device *pdev,
  92. u32 *offset, u32 *stride)
  93. {
  94. struct device *dev = &pdev->dev;
  95. void __iomem *base;
  96. /* All modern platform has offset 0 and stride of 4k */
  97. *offset = 0;
  98. *stride = 0x1000;
  99. base = devm_platform_ioremap_resource(pdev, 0);
  100. if (IS_ERR(base))
  101. return ERR_CAST(base);
  102. return devm_regmap_init_mmio(dev, base, &tcsr_mutex_config);
  103. }
  104. static int qcom_hwspinlock_probe(struct platform_device *pdev)
  105. {
  106. struct hwspinlock_device *bank;
  107. struct reg_field field;
  108. struct regmap *regmap;
  109. size_t array_size;
  110. u32 stride;
  111. u32 base;
  112. int i;
  113. regmap = qcom_hwspinlock_probe_syscon(pdev, &base, &stride);
  114. if (IS_ERR(regmap) && PTR_ERR(regmap) == -ENODEV)
  115. regmap = qcom_hwspinlock_probe_mmio(pdev, &base, &stride);
  116. if (IS_ERR(regmap))
  117. return PTR_ERR(regmap);
  118. array_size = QCOM_MUTEX_NUM_LOCKS * sizeof(struct hwspinlock);
  119. bank = devm_kzalloc(&pdev->dev, sizeof(*bank) + array_size, GFP_KERNEL);
  120. if (!bank)
  121. return -ENOMEM;
  122. platform_set_drvdata(pdev, bank);
  123. for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) {
  124. field.reg = base + i * stride;
  125. field.lsb = 0;
  126. field.msb = 31;
  127. bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev,
  128. regmap, field);
  129. }
  130. return devm_hwspin_lock_register(&pdev->dev, bank, &qcom_hwspinlock_ops,
  131. 0, QCOM_MUTEX_NUM_LOCKS);
  132. }
  133. static struct platform_driver qcom_hwspinlock_driver = {
  134. .probe = qcom_hwspinlock_probe,
  135. .driver = {
  136. .name = "qcom_hwspinlock",
  137. .of_match_table = qcom_hwspinlock_of_match,
  138. },
  139. };
  140. static int __init qcom_hwspinlock_init(void)
  141. {
  142. return platform_driver_register(&qcom_hwspinlock_driver);
  143. }
  144. /* board init code might need to reserve hwspinlocks for predefined purposes */
  145. postcore_initcall(qcom_hwspinlock_init);
  146. static void __exit qcom_hwspinlock_exit(void)
  147. {
  148. platform_driver_unregister(&qcom_hwspinlock_driver);
  149. }
  150. module_exit(qcom_hwspinlock_exit);
  151. MODULE_LICENSE("GPL v2");
  152. MODULE_DESCRIPTION("Hardware spinlock driver for Qualcomm SoCs");