qcom-spmi-sdam.c 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. /* Copyright (c) 2017 The Linux Foundation. All rights reserved.
  2. *
  3. * This program is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License version 2 and
  5. * only version 2 as published by the Free Software Foundation.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. */
  12. #include <linux/device.h>
  13. #include <linux/module.h>
  14. #include <linux/of.h>
  15. #include <linux/of_platform.h>
  16. #include <linux/nvmem-provider.h>
  17. #include <linux/regmap.h>
  18. #define SDAM_MEM_START 0x40
  19. #define REGISTER_MAP_ID 0x40
  20. #define REGISTER_MAP_VERSION 0x41
  21. #define SDAM_SIZE 0x44
  22. #define SDAM_PBS_TRIG_SET 0xE5
  23. #define SDAM_PBS_TRIG_CLR 0xE6
  24. struct sdam_chip {
  25. struct platform_device *pdev;
  26. struct regmap *regmap;
  27. int base;
  28. int size;
  29. };
  30. /* read only register offsets */
  31. static const u8 sdam_ro_map[] = {
  32. REGISTER_MAP_ID,
  33. REGISTER_MAP_VERSION,
  34. SDAM_SIZE
  35. };
  36. static bool is_valid(struct sdam_chip *sdam, unsigned int offset, size_t len)
  37. {
  38. int sdam_mem_end = SDAM_MEM_START + sdam->size - 1;
  39. if (!len)
  40. return false;
  41. if (offset >= SDAM_MEM_START && offset <= sdam_mem_end
  42. && (offset + len - 1) <= sdam_mem_end)
  43. return true;
  44. else if ((offset == SDAM_PBS_TRIG_SET || offset == SDAM_PBS_TRIG_CLR)
  45. && (len == 1))
  46. return true;
  47. return false;
  48. }
  49. static bool is_ro(unsigned int offset, size_t len)
  50. {
  51. int i;
  52. for (i = 0; i < ARRAY_SIZE(sdam_ro_map); i++)
  53. if (offset <= sdam_ro_map[i] && (offset + len) > sdam_ro_map[i])
  54. return true;
  55. return false;
  56. }
  57. static int sdam_read(void *priv, unsigned int offset, void *val, size_t bytes)
  58. {
  59. struct sdam_chip *sdam = priv;
  60. int rc;
  61. if (!is_valid(sdam, offset, bytes)) {
  62. pr_err("Invalid SDAM offset 0x%02x len=%zd\n", offset, bytes);
  63. return -EINVAL;
  64. }
  65. rc = regmap_bulk_read(sdam->regmap, sdam->base + offset, val, bytes);
  66. if (rc < 0)
  67. pr_err("Failed to read SDAM offset 0x%02x len=%zd, rc=%d\n",
  68. offset, bytes, rc);
  69. return rc;
  70. }
  71. static int sdam_write(void *priv, unsigned int offset, void *val, size_t bytes)
  72. {
  73. struct sdam_chip *sdam = priv;
  74. int rc;
  75. if (!is_valid(sdam, offset, bytes)) {
  76. pr_err("Invalid SDAM offset 0x%02x len=%zd\n", offset, bytes);
  77. return -EINVAL;
  78. }
  79. if (is_ro(offset, bytes)) {
  80. pr_err("Invalid write offset 0x%02x len=%zd\n", offset, bytes);
  81. return -EINVAL;
  82. }
  83. rc = regmap_bulk_write(sdam->regmap, sdam->base + offset, val, bytes);
  84. if (rc < 0)
  85. pr_err("Failed to write SDAM offset 0x%02x len=%zd, rc=%d\n",
  86. offset, bytes, rc);
  87. return rc;
  88. }
  89. static int sdam_probe(struct platform_device *pdev)
  90. {
  91. struct sdam_chip *sdam;
  92. struct nvmem_device *nvmem;
  93. struct nvmem_config *sdam_config;
  94. unsigned int val = 0;
  95. int rc;
  96. sdam = devm_kzalloc(&pdev->dev, sizeof(*sdam), GFP_KERNEL);
  97. if (!sdam)
  98. return -ENOMEM;
  99. sdam_config = devm_kzalloc(&pdev->dev, sizeof(*sdam_config),
  100. GFP_KERNEL);
  101. if (!sdam_config)
  102. return -ENOMEM;
  103. sdam->regmap = dev_get_regmap(pdev->dev.parent, NULL);
  104. if (!sdam->regmap) {
  105. pr_err("Failed to get regmap handle\n");
  106. return -ENXIO;
  107. }
  108. rc = of_property_read_u32(pdev->dev.of_node, "reg", &sdam->base);
  109. if (rc < 0) {
  110. pr_err("Failed to get SDAM base, rc=%d\n", rc);
  111. return -EINVAL;
  112. }
  113. rc = regmap_read(sdam->regmap, sdam->base + SDAM_SIZE, &val);
  114. if (rc < 0) {
  115. pr_err("Failed to read SDAM_SIZE rc=%d\n", rc);
  116. return -EINVAL;
  117. }
  118. sdam->size = val * 32;
  119. sdam_config->dev = &pdev->dev;
  120. sdam_config->name = "spmi_sdam";
  121. sdam_config->id = pdev->id;
  122. sdam_config->owner = THIS_MODULE,
  123. sdam_config->stride = 1;
  124. sdam_config->word_size = 1;
  125. sdam_config->reg_read = sdam_read;
  126. sdam_config->reg_write = sdam_write;
  127. sdam_config->priv = sdam;
  128. nvmem = nvmem_register(sdam_config);
  129. if (IS_ERR(nvmem)) {
  130. pr_err("Failed to register SDAM nvmem device rc=%ld\n",
  131. PTR_ERR(nvmem));
  132. return -ENXIO;
  133. }
  134. platform_set_drvdata(pdev, nvmem);
  135. pr_info("SDAM base=0x%04x size=%d registered successfully\n",
  136. sdam->base, sdam->size);
  137. return 0;
  138. }
  139. static int sdam_remove(struct platform_device *pdev)
  140. {
  141. struct nvmem_device *nvmem = platform_get_drvdata(pdev);
  142. return nvmem_unregister(nvmem);
  143. }
  144. static const struct of_device_id sdam_match_table[] = {
  145. {.compatible = "qcom,spmi-sdam"},
  146. {},
  147. };
  148. static struct platform_driver sdam_driver = {
  149. .driver = {
  150. .name = "qcom,spmi-sdam",
  151. .owner = THIS_MODULE,
  152. .of_match_table = sdam_match_table,
  153. },
  154. .probe = sdam_probe,
  155. .remove = sdam_remove,
  156. };
  157. static int __init sdam_init(void)
  158. {
  159. return platform_driver_register(&sdam_driver);
  160. }
  161. subsys_initcall(sdam_init);
  162. static void __exit sdam_exit(void)
  163. {
  164. return platform_driver_unregister(&sdam_driver);
  165. }
  166. module_exit(sdam_exit);
  167. MODULE_DESCRIPTION("QCOM SPMI SDAM driver");
  168. MODULE_LICENSE("GPL v2");