da8xx-ddrctl.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * TI da8xx DDR2/mDDR controller driver
  4. *
  5. * Copyright (C) 2016 BayLibre SAS
  6. *
  7. * Author:
  8. * Bartosz Golaszewski <bgolaszewski@baylibre.com>
  9. */
  10. #include <linux/module.h>
  11. #include <linux/of.h>
  12. #include <linux/platform_device.h>
  13. #include <linux/io.h>
  14. /*
  15. * REVISIT: Linux doesn't have a good framework for the kind of performance
  16. * knobs this driver controls. We can't use device tree properties as it deals
  17. * with hardware configuration rather than description. We also don't want to
  18. * commit to maintaining some random sysfs attributes.
  19. *
  20. * For now we just hardcode the register values for the boards that need
  21. * some changes (as is the case for the LCD controller on da850-lcdk - the
  22. * first board we support here). When linux gets an appropriate framework,
  23. * we'll easily convert the driver to it.
  24. */
  25. struct da8xx_ddrctl_config_knob {
  26. const char *name;
  27. u32 reg;
  28. u32 mask;
  29. u32 shift;
  30. };
  31. static const struct da8xx_ddrctl_config_knob da8xx_ddrctl_knobs[] = {
  32. {
  33. .name = "da850-pbbpr",
  34. .reg = 0x20,
  35. .mask = 0xffffff00,
  36. .shift = 0,
  37. },
  38. };
  39. struct da8xx_ddrctl_setting {
  40. const char *name;
  41. u32 val;
  42. };
  43. struct da8xx_ddrctl_board_settings {
  44. const char *board;
  45. const struct da8xx_ddrctl_setting *settings;
  46. };
  47. static const struct da8xx_ddrctl_setting da850_lcdk_ddrctl_settings[] = {
  48. {
  49. .name = "da850-pbbpr",
  50. .val = 0x20,
  51. },
  52. { }
  53. };
  54. static const struct da8xx_ddrctl_board_settings da8xx_ddrctl_board_confs[] = {
  55. {
  56. .board = "ti,da850-lcdk",
  57. .settings = da850_lcdk_ddrctl_settings,
  58. },
  59. };
  60. static const struct da8xx_ddrctl_config_knob *
  61. da8xx_ddrctl_match_knob(const struct da8xx_ddrctl_setting *setting)
  62. {
  63. const struct da8xx_ddrctl_config_knob *knob;
  64. int i;
  65. for (i = 0; i < ARRAY_SIZE(da8xx_ddrctl_knobs); i++) {
  66. knob = &da8xx_ddrctl_knobs[i];
  67. if (strcmp(knob->name, setting->name) == 0)
  68. return knob;
  69. }
  70. return NULL;
  71. }
  72. static const struct da8xx_ddrctl_setting *da8xx_ddrctl_get_board_settings(void)
  73. {
  74. const struct da8xx_ddrctl_board_settings *board_settings;
  75. int i;
  76. for (i = 0; i < ARRAY_SIZE(da8xx_ddrctl_board_confs); i++) {
  77. board_settings = &da8xx_ddrctl_board_confs[i];
  78. if (of_machine_is_compatible(board_settings->board))
  79. return board_settings->settings;
  80. }
  81. return NULL;
  82. }
  83. static int da8xx_ddrctl_probe(struct platform_device *pdev)
  84. {
  85. const struct da8xx_ddrctl_config_knob *knob;
  86. const struct da8xx_ddrctl_setting *setting;
  87. struct resource *res;
  88. void __iomem *ddrctl;
  89. struct device *dev;
  90. u32 reg;
  91. dev = &pdev->dev;
  92. setting = da8xx_ddrctl_get_board_settings();
  93. if (!setting) {
  94. dev_err(dev, "no settings defined for this board\n");
  95. return -EINVAL;
  96. }
  97. ddrctl = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
  98. if (IS_ERR(ddrctl)) {
  99. dev_err(dev, "unable to map memory controller registers\n");
  100. return PTR_ERR(ddrctl);
  101. }
  102. for (; setting->name; setting++) {
  103. knob = da8xx_ddrctl_match_knob(setting);
  104. if (!knob) {
  105. dev_warn(dev,
  106. "no such config option: %s\n", setting->name);
  107. continue;
  108. }
  109. if (knob->reg + sizeof(u32) > resource_size(res)) {
  110. dev_warn(dev,
  111. "register offset of '%s' exceeds mapped memory size\n",
  112. knob->name);
  113. continue;
  114. }
  115. reg = readl(ddrctl + knob->reg);
  116. reg &= knob->mask;
  117. reg |= setting->val << knob->shift;
  118. dev_dbg(dev, "writing 0x%08x to %s\n", reg, setting->name);
  119. writel(reg, ddrctl + knob->reg);
  120. }
  121. return 0;
  122. }
  123. static const struct of_device_id da8xx_ddrctl_of_match[] = {
  124. { .compatible = "ti,da850-ddr-controller", },
  125. { },
  126. };
  127. static struct platform_driver da8xx_ddrctl_driver = {
  128. .probe = da8xx_ddrctl_probe,
  129. .driver = {
  130. .name = "da850-ddr-controller",
  131. .of_match_table = da8xx_ddrctl_of_match,
  132. },
  133. };
  134. module_platform_driver(da8xx_ddrctl_driver);
  135. MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>");
  136. MODULE_DESCRIPTION("TI da8xx DDR2/mDDR controller driver");