arm-device.c 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. /*
  2. * Copyright (c) 2015, Linaro Limited, Shannon Zhao
  3. *
  4. * This program is free software; you can redistribute it and/or modify it
  5. * under the terms and conditions of the GNU General Public License,
  6. * version 2, as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope it will be useful, but WITHOUT
  9. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  11. * more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include <linux/platform_device.h>
  17. #include <linux/acpi.h>
  18. #include <xen/xen.h>
  19. #include <xen/page.h>
  20. #include <xen/interface/memory.h>
  21. #include <asm/xen/hypervisor.h>
  22. #include <asm/xen/hypercall.h>
  23. static int xen_unmap_device_mmio(const struct resource *resources,
  24. unsigned int count)
  25. {
  26. unsigned int i, j, nr;
  27. int rc = 0;
  28. const struct resource *r;
  29. struct xen_remove_from_physmap xrp;
  30. for (i = 0; i < count; i++) {
  31. r = &resources[i];
  32. nr = DIV_ROUND_UP(resource_size(r), XEN_PAGE_SIZE);
  33. if ((resource_type(r) != IORESOURCE_MEM) || (nr == 0))
  34. continue;
  35. for (j = 0; j < nr; j++) {
  36. xrp.domid = DOMID_SELF;
  37. xrp.gpfn = XEN_PFN_DOWN(r->start) + j;
  38. rc = HYPERVISOR_memory_op(XENMEM_remove_from_physmap,
  39. &xrp);
  40. if (rc)
  41. return rc;
  42. }
  43. }
  44. return rc;
  45. }
  46. static int xen_map_device_mmio(const struct resource *resources,
  47. unsigned int count)
  48. {
  49. unsigned int i, j, nr;
  50. int rc = 0;
  51. const struct resource *r;
  52. xen_pfn_t *gpfns;
  53. xen_ulong_t *idxs;
  54. int *errs;
  55. struct xen_add_to_physmap_range xatp;
  56. for (i = 0; i < count; i++) {
  57. r = &resources[i];
  58. nr = DIV_ROUND_UP(resource_size(r), XEN_PAGE_SIZE);
  59. if ((resource_type(r) != IORESOURCE_MEM) || (nr == 0))
  60. continue;
  61. gpfns = kzalloc(sizeof(xen_pfn_t) * nr, GFP_KERNEL);
  62. idxs = kzalloc(sizeof(xen_ulong_t) * nr, GFP_KERNEL);
  63. errs = kzalloc(sizeof(int) * nr, GFP_KERNEL);
  64. if (!gpfns || !idxs || !errs) {
  65. kfree(gpfns);
  66. kfree(idxs);
  67. kfree(errs);
  68. rc = -ENOMEM;
  69. goto unmap;
  70. }
  71. for (j = 0; j < nr; j++) {
  72. /*
  73. * The regions are always mapped 1:1 to DOM0 and this is
  74. * fine because the memory map for DOM0 is the same as
  75. * the host (except for the RAM).
  76. */
  77. gpfns[j] = XEN_PFN_DOWN(r->start) + j;
  78. idxs[j] = XEN_PFN_DOWN(r->start) + j;
  79. }
  80. xatp.domid = DOMID_SELF;
  81. xatp.size = nr;
  82. xatp.space = XENMAPSPACE_dev_mmio;
  83. set_xen_guest_handle(xatp.gpfns, gpfns);
  84. set_xen_guest_handle(xatp.idxs, idxs);
  85. set_xen_guest_handle(xatp.errs, errs);
  86. rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap_range, &xatp);
  87. kfree(gpfns);
  88. kfree(idxs);
  89. kfree(errs);
  90. if (rc)
  91. goto unmap;
  92. }
  93. return rc;
  94. unmap:
  95. xen_unmap_device_mmio(resources, i);
  96. return rc;
  97. }
  98. static int xen_platform_notifier(struct notifier_block *nb,
  99. unsigned long action, void *data)
  100. {
  101. struct platform_device *pdev = to_platform_device(data);
  102. int r = 0;
  103. if (pdev->num_resources == 0 || pdev->resource == NULL)
  104. return NOTIFY_OK;
  105. switch (action) {
  106. case BUS_NOTIFY_ADD_DEVICE:
  107. r = xen_map_device_mmio(pdev->resource, pdev->num_resources);
  108. break;
  109. case BUS_NOTIFY_DEL_DEVICE:
  110. r = xen_unmap_device_mmio(pdev->resource, pdev->num_resources);
  111. break;
  112. default:
  113. return NOTIFY_DONE;
  114. }
  115. if (r)
  116. dev_err(&pdev->dev, "Platform: Failed to %s device %s MMIO!\n",
  117. action == BUS_NOTIFY_ADD_DEVICE ? "map" :
  118. (action == BUS_NOTIFY_DEL_DEVICE ? "unmap" : "?"),
  119. pdev->name);
  120. return NOTIFY_OK;
  121. }
  122. static struct notifier_block platform_device_nb = {
  123. .notifier_call = xen_platform_notifier,
  124. };
  125. static int __init register_xen_platform_notifier(void)
  126. {
  127. if (!xen_initial_domain() || acpi_disabled)
  128. return 0;
  129. return bus_register_notifier(&platform_bus_type, &platform_device_nb);
  130. }
  131. arch_initcall(register_xen_platform_notifier);
  132. #ifdef CONFIG_ARM_AMBA
  133. #include <linux/amba/bus.h>
  134. static int xen_amba_notifier(struct notifier_block *nb,
  135. unsigned long action, void *data)
  136. {
  137. struct amba_device *adev = to_amba_device(data);
  138. int r = 0;
  139. switch (action) {
  140. case BUS_NOTIFY_ADD_DEVICE:
  141. r = xen_map_device_mmio(&adev->res, 1);
  142. break;
  143. case BUS_NOTIFY_DEL_DEVICE:
  144. r = xen_unmap_device_mmio(&adev->res, 1);
  145. break;
  146. default:
  147. return NOTIFY_DONE;
  148. }
  149. if (r)
  150. dev_err(&adev->dev, "AMBA: Failed to %s device %s MMIO!\n",
  151. action == BUS_NOTIFY_ADD_DEVICE ? "map" :
  152. (action == BUS_NOTIFY_DEL_DEVICE ? "unmap" : "?"),
  153. adev->dev.init_name);
  154. return NOTIFY_OK;
  155. }
  156. static struct notifier_block amba_device_nb = {
  157. .notifier_call = xen_amba_notifier,
  158. };
  159. static int __init register_xen_amba_notifier(void)
  160. {
  161. if (!xen_initial_domain() || acpi_disabled)
  162. return 0;
  163. return bus_register_notifier(&amba_bustype, &amba_device_nb);
  164. }
  165. arch_initcall(register_xen_amba_notifier);
  166. #endif