clk-uniphier-core.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /*
  2. * Copyright (C) 2016 Socionext Inc.
  3. * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
  4. *
  5. * SPDX-License-Identifier: GPL-2.0+
  6. */
  7. #include <common.h>
  8. #include <linux/bitops.h>
  9. #include <linux/io.h>
  10. #include <linux/sizes.h>
  11. #include <clk-uclass.h>
  12. #include <dm/device.h>
  13. #include "clk-uniphier.h"
  14. /**
  15. * struct uniphier_clk_priv - private data for UniPhier clock driver
  16. *
  17. * @base: base address of the clock provider
  18. * @socdata: SoC specific data
  19. */
  20. struct uniphier_clk_priv {
  21. void __iomem *base;
  22. const struct uniphier_clk_soc_data *socdata;
  23. };
  24. int uniphier_clk_probe(struct udevice *dev)
  25. {
  26. struct uniphier_clk_priv *priv = dev_get_priv(dev);
  27. fdt_addr_t addr;
  28. addr = dev_get_addr(dev->parent);
  29. if (addr == FDT_ADDR_T_NONE)
  30. return -EINVAL;
  31. priv->base = devm_ioremap(dev, addr, SZ_4K);
  32. if (!priv->base)
  33. return -ENOMEM;
  34. priv->socdata = (void *)dev_get_driver_data(dev);
  35. return 0;
  36. }
  37. static int uniphier_clk_enable(struct clk *clk)
  38. {
  39. struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
  40. const struct uniphier_clk_gate_data *gate = priv->socdata->gate;
  41. unsigned int nr_gate = priv->socdata->nr_gate;
  42. void __iomem *reg;
  43. u32 mask, data, tmp;
  44. int i;
  45. for (i = 0; i < nr_gate; i++) {
  46. if (gate[i].index != clk->id)
  47. continue;
  48. reg = priv->base + gate[i].reg;
  49. mask = gate[i].mask;
  50. data = gate[i].data & mask;
  51. tmp = readl(reg);
  52. tmp &= ~mask;
  53. tmp |= data & mask;
  54. debug("%s: %p: %08x\n", __func__, reg, tmp);
  55. writel(tmp, reg);
  56. }
  57. return 0;
  58. }
  59. static ulong uniphier_clk_get_rate(struct clk *clk)
  60. {
  61. struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
  62. const struct uniphier_clk_rate_data *rdata = priv->socdata->rate;
  63. unsigned int nr_rdata = priv->socdata->nr_rate;
  64. void __iomem *reg;
  65. u32 mask, data;
  66. ulong matched_rate = 0;
  67. int i;
  68. for (i = 0; i < nr_rdata; i++) {
  69. if (rdata[i].index != clk->id)
  70. continue;
  71. if (rdata[i].reg == UNIPHIER_CLK_RATE_IS_FIXED)
  72. return rdata[i].rate;
  73. reg = priv->base + rdata[i].reg;
  74. mask = rdata[i].mask;
  75. data = rdata[i].data & mask;
  76. if ((readl(reg) & mask) == data) {
  77. if (matched_rate && rdata[i].rate != matched_rate) {
  78. printf("failed to get clk rate for insane register values\n");
  79. return -EINVAL;
  80. }
  81. matched_rate = rdata[i].rate;
  82. }
  83. }
  84. debug("%s: rate = %lu\n", __func__, matched_rate);
  85. return matched_rate;
  86. }
  87. static ulong uniphier_clk_set_rate(struct clk *clk, ulong rate)
  88. {
  89. struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
  90. const struct uniphier_clk_rate_data *rdata = priv->socdata->rate;
  91. unsigned int nr_rdata = priv->socdata->nr_rate;
  92. void __iomem *reg;
  93. u32 mask, data, tmp;
  94. ulong best_rate = 0;
  95. int i;
  96. /* first, decide the best match rate */
  97. for (i = 0; i < nr_rdata; i++) {
  98. if (rdata[i].index != clk->id)
  99. continue;
  100. if (rdata[i].reg == UNIPHIER_CLK_RATE_IS_FIXED)
  101. return 0;
  102. if (rdata[i].rate > best_rate && rdata[i].rate <= rate)
  103. best_rate = rdata[i].rate;
  104. }
  105. if (!best_rate)
  106. return -ENODEV;
  107. debug("%s: requested rate = %lu, set rate = %lu\n", __func__,
  108. rate, best_rate);
  109. /* second, really set registers */
  110. for (i = 0; i < nr_rdata; i++) {
  111. if (rdata[i].index != clk->id || rdata[i].rate != best_rate)
  112. continue;
  113. reg = priv->base + rdata[i].reg;
  114. mask = rdata[i].mask;
  115. data = rdata[i].data & mask;
  116. tmp = readl(reg);
  117. tmp &= ~mask;
  118. tmp |= data;
  119. debug("%s: %p: %08x\n", __func__, reg, tmp);
  120. writel(tmp, reg);
  121. }
  122. return best_rate;
  123. }
  124. const struct clk_ops uniphier_clk_ops = {
  125. .enable = uniphier_clk_enable,
  126. .get_rate = uniphier_clk_get_rate,
  127. .set_rate = uniphier_clk_set_rate,
  128. };
  129. static const struct udevice_id uniphier_clk_match[] = {
  130. {
  131. .compatible = "socionext,uniphier-sld3-mio-clock",
  132. .data = (ulong)&uniphier_mio_clk_data,
  133. },
  134. {
  135. .compatible = "socionext,uniphier-ld4-mio-clock",
  136. .data = (ulong)&uniphier_mio_clk_data,
  137. },
  138. {
  139. .compatible = "socionext,uniphier-pro4-mio-clock",
  140. .data = (ulong)&uniphier_mio_clk_data,
  141. },
  142. {
  143. .compatible = "socionext,uniphier-sld8-mio-clock",
  144. .data = (ulong)&uniphier_mio_clk_data,
  145. },
  146. {
  147. .compatible = "socionext,uniphier-pro5-mio-clock",
  148. .data = (ulong)&uniphier_mio_clk_data,
  149. },
  150. {
  151. .compatible = "socionext,uniphier-pxs2-mio-clock",
  152. .data = (ulong)&uniphier_mio_clk_data,
  153. },
  154. {
  155. .compatible = "socionext,uniphier-ld11-mio-clock",
  156. .data = (ulong)&uniphier_mio_clk_data,
  157. },
  158. {
  159. .compatible = "socionext,uniphier-ld20-mio-clock",
  160. .data = (ulong)&uniphier_mio_clk_data,
  161. },
  162. { /* sentinel */ }
  163. };
  164. U_BOOT_DRIVER(uniphier_clk) = {
  165. .name = "uniphier-clk",
  166. .id = UCLASS_CLK,
  167. .of_match = uniphier_clk_match,
  168. .probe = uniphier_clk_probe,
  169. .priv_auto_alloc_size = sizeof(struct uniphier_clk_priv),
  170. .ops = &uniphier_clk_ops,
  171. };