clk-uniphier-cpugear.c 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. /*
  2. * Copyright (C) 2016 Socionext Inc.
  3. * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. */
  15. #include <linux/clk-provider.h>
  16. #include <linux/device.h>
  17. #include <linux/regmap.h>
  18. #include "clk-uniphier.h"
  19. #define UNIPHIER_CLK_CPUGEAR_STAT 0 /* status */
  20. #define UNIPHIER_CLK_CPUGEAR_SET 4 /* set */
  21. #define UNIPHIER_CLK_CPUGEAR_UPD 8 /* update */
  22. #define UNIPHIER_CLK_CPUGEAR_UPD_BIT BIT(0)
  23. struct uniphier_clk_cpugear {
  24. struct clk_hw hw;
  25. struct regmap *regmap;
  26. unsigned int regbase;
  27. unsigned int mask;
  28. };
  29. #define to_uniphier_clk_cpugear(_hw) \
  30. container_of(_hw, struct uniphier_clk_cpugear, hw)
  31. static int uniphier_clk_cpugear_set_parent(struct clk_hw *hw, u8 index)
  32. {
  33. struct uniphier_clk_cpugear *gear = to_uniphier_clk_cpugear(hw);
  34. int ret;
  35. unsigned int val;
  36. ret = regmap_write_bits(gear->regmap,
  37. gear->regbase + UNIPHIER_CLK_CPUGEAR_SET,
  38. gear->mask, index);
  39. if (ret)
  40. return ret;
  41. ret = regmap_write_bits(gear->regmap,
  42. gear->regbase + UNIPHIER_CLK_CPUGEAR_UPD,
  43. UNIPHIER_CLK_CPUGEAR_UPD_BIT,
  44. UNIPHIER_CLK_CPUGEAR_UPD_BIT);
  45. if (ret)
  46. return ret;
  47. return regmap_read_poll_timeout(gear->regmap,
  48. gear->regbase + UNIPHIER_CLK_CPUGEAR_UPD,
  49. val, !(val & UNIPHIER_CLK_CPUGEAR_UPD_BIT),
  50. 0, 1);
  51. }
  52. static u8 uniphier_clk_cpugear_get_parent(struct clk_hw *hw)
  53. {
  54. struct uniphier_clk_cpugear *gear = to_uniphier_clk_cpugear(hw);
  55. int num_parents = clk_hw_get_num_parents(hw);
  56. int ret;
  57. unsigned int val;
  58. ret = regmap_read(gear->regmap,
  59. gear->regbase + UNIPHIER_CLK_CPUGEAR_STAT, &val);
  60. if (ret)
  61. return ret;
  62. val &= gear->mask;
  63. return val < num_parents ? val : -EINVAL;
  64. }
  65. static const struct clk_ops uniphier_clk_cpugear_ops = {
  66. .determine_rate = __clk_mux_determine_rate,
  67. .set_parent = uniphier_clk_cpugear_set_parent,
  68. .get_parent = uniphier_clk_cpugear_get_parent,
  69. };
  70. struct clk_hw *uniphier_clk_register_cpugear(struct device *dev,
  71. struct regmap *regmap,
  72. const char *name,
  73. const struct uniphier_clk_cpugear_data *data)
  74. {
  75. struct uniphier_clk_cpugear *gear;
  76. struct clk_init_data init;
  77. int ret;
  78. gear = devm_kzalloc(dev, sizeof(*gear), GFP_KERNEL);
  79. if (!gear)
  80. return ERR_PTR(-ENOMEM);
  81. init.name = name;
  82. init.ops = &uniphier_clk_cpugear_ops;
  83. init.flags = CLK_SET_RATE_PARENT;
  84. init.parent_names = data->parent_names;
  85. init.num_parents = data->num_parents,
  86. gear->regmap = regmap;
  87. gear->regbase = data->regbase;
  88. gear->mask = data->mask;
  89. gear->hw.init = &init;
  90. ret = devm_clk_hw_register(dev, &gear->hw);
  91. if (ret)
  92. return ERR_PTR(ret);
  93. return &gear->hw;
  94. }