qpnp-misc.c 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. /* Copyright (c) 2013, 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. #define pr_fmt(fmt) "%s: " fmt, __func__
  13. #include <linux/module.h>
  14. #include <linux/err.h>
  15. #include <linux/slab.h>
  16. #include <linux/spmi.h>
  17. #include <linux/of.h>
  18. #include <linux/of_device.h>
  19. #include <linux/qpnp-misc.h>
  20. #define QPNP_MISC_DEV_NAME "qcom,qpnp-misc"
  21. #define REG_DIG_MAJOR_REV 0x01
  22. #define REG_SUBTYPE 0x05
  23. static DEFINE_MUTEX(qpnp_misc_dev_list_mutex);
  24. static LIST_HEAD(qpnp_misc_dev_list);
  25. /**
  26. * struct qpnp_misc_dev - holds controller device specific information
  27. * @list: Doubly-linked list parameter linking to other
  28. * qpnp_misc devices.
  29. * @mutex: Mutex lock that is used to ensure mutual
  30. * exclusion between probing and accessing misc
  31. * driver information
  32. * @dev: Device pointer to the misc device
  33. * @resource: Resource pointer that holds base address
  34. * @spmi: Spmi pointer which holds spmi information
  35. */
  36. struct qpnp_misc_dev {
  37. struct list_head list;
  38. struct mutex mutex;
  39. struct device *dev;
  40. struct resource *resource;
  41. struct spmi_device *spmi;
  42. };
  43. struct qpnp_misc_version {
  44. u8 subtype;
  45. u8 dig_major_rev;
  46. };
  47. static struct of_device_id qpnp_misc_match_table[] = {
  48. { .compatible = QPNP_MISC_DEV_NAME },
  49. {}
  50. };
  51. static u8 qpnp_read_byte(struct spmi_device *spmi, u16 addr)
  52. {
  53. int rc;
  54. u8 val;
  55. rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, addr, &val, 1);
  56. if (rc) {
  57. pr_err("SPMI read failed rc=%d\n", rc);
  58. return 0;
  59. }
  60. return val;
  61. }
  62. static struct qpnp_misc_version irq_support_version[] = {
  63. {0x01, 0x02}, /* PM8941 */
  64. {0x07, 0x00}, /* PM8226 */
  65. {0x09, 0x00}, /* PMA8084 */
  66. };
  67. static bool __misc_irqs_available(struct qpnp_misc_dev *dev)
  68. {
  69. int i;
  70. u8 subtype, dig_major_rev;
  71. subtype = qpnp_read_byte(dev->spmi, dev->resource->start + REG_SUBTYPE);
  72. pr_debug("subtype = 0x%02X\n", subtype);
  73. dig_major_rev = qpnp_read_byte(dev->spmi,
  74. dev->resource->start + REG_DIG_MAJOR_REV);
  75. pr_debug("dig_major rev = 0x%02X\n", dig_major_rev);
  76. for (i = 0; i < ARRAY_SIZE(irq_support_version); i++)
  77. if (subtype == irq_support_version[i].subtype
  78. && dig_major_rev >= irq_support_version[i].dig_major_rev)
  79. return 1;
  80. return 0;
  81. }
  82. int qpnp_misc_irqs_available(struct device *consumer_dev)
  83. {
  84. struct device_node *misc_node = NULL;
  85. struct qpnp_misc_dev *mdev = NULL;
  86. struct qpnp_misc_dev *mdev_found = NULL;
  87. if (IS_ERR_OR_NULL(consumer_dev)) {
  88. pr_err("Invalid consumer device pointer\n");
  89. return -EINVAL;
  90. }
  91. misc_node = of_parse_phandle(consumer_dev->of_node, "qcom,misc-ref", 0);
  92. if (!misc_node) {
  93. pr_debug("Could not find qcom,misc-ref property in %s\n",
  94. consumer_dev->of_node->full_name);
  95. return 0;
  96. }
  97. mutex_lock(&qpnp_misc_dev_list_mutex);
  98. list_for_each_entry(mdev, &qpnp_misc_dev_list, list) {
  99. if (mdev->dev->of_node == misc_node) {
  100. mdev_found = mdev;
  101. break;
  102. }
  103. }
  104. mutex_unlock(&qpnp_misc_dev_list_mutex);
  105. if (!mdev_found) {
  106. /* No MISC device was found. This API should only
  107. * be called by drivers which have specified the
  108. * misc phandle in their device tree node */
  109. pr_err("no probed misc device found\n");
  110. return -EPROBE_DEFER;
  111. }
  112. return __misc_irqs_available(mdev_found);
  113. }
  114. static int __devinit qpnp_misc_probe(struct spmi_device *spmi)
  115. {
  116. struct resource *resource;
  117. struct qpnp_misc_dev *mdev = ERR_PTR(-EINVAL);
  118. resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
  119. if (!resource) {
  120. pr_err("Unable to get spmi resource for MISC\n");
  121. return -EINVAL;
  122. }
  123. mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
  124. if (!mdev) {
  125. pr_err("allocation failed\n");
  126. return -ENOMEM;
  127. }
  128. mdev->spmi = spmi;
  129. mdev->dev = &(spmi->dev);
  130. mdev->resource = resource;
  131. mutex_lock(&qpnp_misc_dev_list_mutex);
  132. list_add_tail(&mdev->list, &qpnp_misc_dev_list);
  133. mutex_unlock(&qpnp_misc_dev_list_mutex);
  134. pr_debug("probed successfully\n");
  135. return 0;
  136. }
  137. static struct spmi_driver qpnp_misc_driver = {
  138. .probe = qpnp_misc_probe,
  139. .driver = {
  140. .name = QPNP_MISC_DEV_NAME,
  141. .owner = THIS_MODULE,
  142. .of_match_table = qpnp_misc_match_table,
  143. },
  144. };
  145. static int __init qpnp_misc_init(void)
  146. {
  147. return spmi_driver_register(&qpnp_misc_driver);
  148. }
  149. static void __exit qpnp_misc_exit(void)
  150. {
  151. return spmi_driver_unregister(&qpnp_misc_driver);
  152. }
  153. module_init(qpnp_misc_init);
  154. module_exit(qpnp_misc_exit);
  155. MODULE_DESCRIPTION(QPNP_MISC_DEV_NAME);
  156. MODULE_LICENSE("GPL v2");
  157. MODULE_ALIAS("platform:" QPNP_MISC_DEV_NAME);