mcb-lpc.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * MEN Chameleon Bus.
  4. *
  5. * Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de)
  6. * Author: Andreas Werner <[email protected]>
  7. */
  8. #include <linux/platform_device.h>
  9. #include <linux/module.h>
  10. #include <linux/dmi.h>
  11. #include <linux/mcb.h>
  12. #include <linux/io.h>
  13. #include "mcb-internal.h"
  14. struct priv {
  15. struct mcb_bus *bus;
  16. struct resource *mem;
  17. void __iomem *base;
  18. };
  19. static int mcb_lpc_probe(struct platform_device *pdev)
  20. {
  21. struct resource *res;
  22. struct priv *priv;
  23. int ret = 0, table_size;
  24. priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
  25. if (!priv)
  26. return -ENOMEM;
  27. priv->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  28. if (!priv->mem) {
  29. dev_err(&pdev->dev, "No Memory resource\n");
  30. return -ENODEV;
  31. }
  32. res = devm_request_mem_region(&pdev->dev, priv->mem->start,
  33. resource_size(priv->mem),
  34. KBUILD_MODNAME);
  35. if (!res) {
  36. dev_err(&pdev->dev, "Failed to request IO memory\n");
  37. return -EBUSY;
  38. }
  39. priv->base = devm_ioremap(&pdev->dev, priv->mem->start,
  40. resource_size(priv->mem));
  41. if (!priv->base) {
  42. dev_err(&pdev->dev, "Cannot ioremap\n");
  43. return -ENOMEM;
  44. }
  45. platform_set_drvdata(pdev, priv);
  46. priv->bus = mcb_alloc_bus(&pdev->dev);
  47. if (IS_ERR(priv->bus))
  48. return PTR_ERR(priv->bus);
  49. ret = chameleon_parse_cells(priv->bus, priv->mem->start, priv->base);
  50. if (ret < 0) {
  51. goto out_mcb_bus;
  52. }
  53. table_size = ret;
  54. if (table_size < CHAM_HEADER_SIZE) {
  55. /* Release the previous resources */
  56. devm_iounmap(&pdev->dev, priv->base);
  57. devm_release_mem_region(&pdev->dev, priv->mem->start, resource_size(priv->mem));
  58. /* Then, allocate it again with the actual chameleon table size */
  59. res = devm_request_mem_region(&pdev->dev, priv->mem->start,
  60. table_size,
  61. KBUILD_MODNAME);
  62. if (!res) {
  63. dev_err(&pdev->dev, "Failed to request PCI memory\n");
  64. ret = -EBUSY;
  65. goto out_mcb_bus;
  66. }
  67. priv->base = devm_ioremap(&pdev->dev, priv->mem->start, table_size);
  68. if (!priv->base) {
  69. dev_err(&pdev->dev, "Cannot ioremap\n");
  70. ret = -ENOMEM;
  71. goto out_mcb_bus;
  72. }
  73. platform_set_drvdata(pdev, priv);
  74. }
  75. mcb_bus_add_devices(priv->bus);
  76. return 0;
  77. out_mcb_bus:
  78. mcb_release_bus(priv->bus);
  79. return ret;
  80. }
  81. static int mcb_lpc_remove(struct platform_device *pdev)
  82. {
  83. struct priv *priv = platform_get_drvdata(pdev);
  84. mcb_release_bus(priv->bus);
  85. return 0;
  86. }
  87. static struct platform_device *mcb_lpc_pdev;
  88. static int mcb_lpc_create_platform_device(const struct dmi_system_id *id)
  89. {
  90. struct resource *res = id->driver_data;
  91. int ret;
  92. mcb_lpc_pdev = platform_device_alloc("mcb-lpc", -1);
  93. if (!mcb_lpc_pdev)
  94. return -ENOMEM;
  95. ret = platform_device_add_resources(mcb_lpc_pdev, res, 1);
  96. if (ret)
  97. goto out_put;
  98. ret = platform_device_add(mcb_lpc_pdev);
  99. if (ret)
  100. goto out_put;
  101. return 0;
  102. out_put:
  103. platform_device_put(mcb_lpc_pdev);
  104. return ret;
  105. }
  106. static struct resource sc24_fpga_resource = DEFINE_RES_MEM(0xe000e000, CHAM_HEADER_SIZE);
  107. static struct resource sc31_fpga_resource = DEFINE_RES_MEM(0xf000e000, CHAM_HEADER_SIZE);
  108. static struct platform_driver mcb_lpc_driver = {
  109. .driver = {
  110. .name = "mcb-lpc",
  111. },
  112. .probe = mcb_lpc_probe,
  113. .remove = mcb_lpc_remove,
  114. };
  115. static const struct dmi_system_id mcb_lpc_dmi_table[] = {
  116. {
  117. .ident = "SC24",
  118. .matches = {
  119. DMI_MATCH(DMI_SYS_VENDOR, "MEN"),
  120. DMI_MATCH(DMI_PRODUCT_VERSION, "14SC24"),
  121. },
  122. .driver_data = (void *)&sc24_fpga_resource,
  123. .callback = mcb_lpc_create_platform_device,
  124. },
  125. {
  126. .ident = "SC31",
  127. .matches = {
  128. DMI_MATCH(DMI_SYS_VENDOR, "MEN"),
  129. DMI_MATCH(DMI_PRODUCT_VERSION, "14SC31"),
  130. },
  131. .driver_data = (void *)&sc31_fpga_resource,
  132. .callback = mcb_lpc_create_platform_device,
  133. },
  134. {}
  135. };
  136. MODULE_DEVICE_TABLE(dmi, mcb_lpc_dmi_table);
  137. static int __init mcb_lpc_init(void)
  138. {
  139. if (!dmi_check_system(mcb_lpc_dmi_table))
  140. return -ENODEV;
  141. return platform_driver_register(&mcb_lpc_driver);
  142. }
  143. static void __exit mcb_lpc_exit(void)
  144. {
  145. platform_device_unregister(mcb_lpc_pdev);
  146. platform_driver_unregister(&mcb_lpc_driver);
  147. }
  148. module_init(mcb_lpc_init);
  149. module_exit(mcb_lpc_exit);
  150. MODULE_AUTHOR("Andreas Werner <[email protected]>");
  151. MODULE_LICENSE("GPL");
  152. MODULE_DESCRIPTION("MCB over LPC support");
  153. MODULE_IMPORT_NS(MCB);