cd-gpio.c 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. /*
  2. * Generic GPIO card-detect helper
  3. *
  4. * Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License version 2 as
  8. * published by the Free Software Foundation.
  9. */
  10. #include <linux/err.h>
  11. #include <linux/gpio.h>
  12. #include <linux/interrupt.h>
  13. #include <linux/jiffies.h>
  14. #include <linux/mmc/cd-gpio.h>
  15. #include <linux/mmc/host.h>
  16. #include <linux/module.h>
  17. #include <linux/slab.h>
  18. struct mmc_cd_gpio {
  19. unsigned int gpio;
  20. bool status;
  21. char label[0];
  22. };
  23. static int mmc_cd_get_status(struct mmc_host *host)
  24. {
  25. int ret = -ENOSYS;
  26. struct mmc_cd_gpio *cd = host->hotplug.handler_priv;
  27. if (!cd || !gpio_is_valid(cd->gpio))
  28. goto out;
  29. ret = !gpio_get_value_cansleep(cd->gpio) ^
  30. !!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH);
  31. out:
  32. return ret;
  33. }
  34. static irqreturn_t mmc_cd_gpio_irqt(int irq, void *dev_id)
  35. {
  36. struct mmc_host *host = dev_id;
  37. struct mmc_cd_gpio *cd = host->hotplug.handler_priv;
  38. int status;
  39. status = mmc_cd_get_status(host);
  40. if (unlikely(status < 0))
  41. goto out;
  42. if (status ^ cd->status) {
  43. pr_info("%s: slot status change detected (%d -> %d), GPIO_ACTIVE_%s\n",
  44. mmc_hostname(host), cd->status, status,
  45. (host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH) ?
  46. "HIGH" : "LOW");
  47. cd->status = status;
  48. /* Schedule a card detection after a debounce timeout */
  49. mmc_detect_change(host, msecs_to_jiffies(100));
  50. }
  51. out:
  52. return IRQ_HANDLED;
  53. }
  54. int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio)
  55. {
  56. size_t len = strlen(dev_name(host->parent)) + 4;
  57. struct mmc_cd_gpio *cd;
  58. int irq = gpio_to_irq(gpio);
  59. int ret;
  60. if (irq < 0)
  61. return irq;
  62. cd = kmalloc(sizeof(*cd) + len, GFP_KERNEL);
  63. if (!cd) {
  64. host->hotplug.handler_priv = NULL;
  65. host->hotplug.irq = 0;
  66. return -ENOMEM;
  67. }
  68. snprintf(cd->label, len, "%s cd", dev_name(host->parent));
  69. ret = gpio_request_one(gpio, GPIOF_DIR_IN, cd->label);
  70. if (ret < 0)
  71. goto egpioreq;
  72. cd->gpio = gpio;
  73. host->hotplug.irq = irq;
  74. host->hotplug.handler_priv = cd;
  75. ret = mmc_cd_get_status(host);
  76. if (ret < 0)
  77. goto eirqreq;
  78. cd->status = ret;
  79. ret = request_threaded_irq(irq, NULL, mmc_cd_gpio_irqt,
  80. IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
  81. cd->label, host);
  82. if (ret < 0)
  83. goto eirqreq;
  84. return 0;
  85. eirqreq:
  86. host->hotplug.handler_priv = NULL;
  87. host->hotplug.irq = 0;
  88. gpio_free(gpio);
  89. egpioreq:
  90. kfree(cd);
  91. return ret;
  92. }
  93. EXPORT_SYMBOL(mmc_cd_gpio_request);
  94. void mmc_cd_gpio_free(struct mmc_host *host)
  95. {
  96. struct mmc_cd_gpio *cd = host->hotplug.handler_priv;
  97. if (!cd || !gpio_is_valid(cd->gpio))
  98. return;
  99. free_irq(host->hotplug.irq, host);
  100. gpio_free(cd->gpio);
  101. cd->gpio = -EINVAL;
  102. kfree(cd);
  103. host->hotplug.handler_priv = NULL;
  104. }
  105. EXPORT_SYMBOL(mmc_cd_gpio_free);