da8xx-ddrctl.c 3.9 KB

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