clk-uniphier-cpugear.c 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Copyright (C) 2016 Socionext Inc.
  4. * Author: Masahiro Yamada <[email protected]>
  5. */
  6. #include <linux/clk-provider.h>
  7. #include <linux/device.h>
  8. #include <linux/regmap.h>
  9. #include "clk-uniphier.h"
  10. #define UNIPHIER_CLK_CPUGEAR_STAT 0 /* status */
  11. #define UNIPHIER_CLK_CPUGEAR_SET 4 /* set */
  12. #define UNIPHIER_CLK_CPUGEAR_UPD 8 /* update */
  13. #define UNIPHIER_CLK_CPUGEAR_UPD_BIT BIT(0)
  14. struct uniphier_clk_cpugear {
  15. struct clk_hw hw;
  16. struct regmap *regmap;
  17. unsigned int regbase;
  18. unsigned int mask;
  19. };
  20. #define to_uniphier_clk_cpugear(_hw) \
  21. container_of(_hw, struct uniphier_clk_cpugear, hw)
  22. static int uniphier_clk_cpugear_set_parent(struct clk_hw *hw, u8 index)
  23. {
  24. struct uniphier_clk_cpugear *gear = to_uniphier_clk_cpugear(hw);
  25. int ret;
  26. unsigned int val;
  27. ret = regmap_write_bits(gear->regmap,
  28. gear->regbase + UNIPHIER_CLK_CPUGEAR_SET,
  29. gear->mask, index);
  30. if (ret)
  31. return ret;
  32. ret = regmap_write_bits(gear->regmap,
  33. gear->regbase + UNIPHIER_CLK_CPUGEAR_UPD,
  34. UNIPHIER_CLK_CPUGEAR_UPD_BIT,
  35. UNIPHIER_CLK_CPUGEAR_UPD_BIT);
  36. if (ret)
  37. return ret;
  38. return regmap_read_poll_timeout(gear->regmap,
  39. gear->regbase + UNIPHIER_CLK_CPUGEAR_UPD,
  40. val, !(val & UNIPHIER_CLK_CPUGEAR_UPD_BIT),
  41. 0, 1);
  42. }
  43. static u8 uniphier_clk_cpugear_get_parent(struct clk_hw *hw)
  44. {
  45. struct uniphier_clk_cpugear *gear = to_uniphier_clk_cpugear(hw);
  46. int num_parents = clk_hw_get_num_parents(hw);
  47. int ret;
  48. unsigned int val;
  49. ret = regmap_read(gear->regmap,
  50. gear->regbase + UNIPHIER_CLK_CPUGEAR_STAT, &val);
  51. if (ret)
  52. return ret;
  53. val &= gear->mask;
  54. return val < num_parents ? val : -EINVAL;
  55. }
  56. static const struct clk_ops uniphier_clk_cpugear_ops = {
  57. .determine_rate = __clk_mux_determine_rate,
  58. .set_parent = uniphier_clk_cpugear_set_parent,
  59. .get_parent = uniphier_clk_cpugear_get_parent,
  60. };
  61. struct clk_hw *uniphier_clk_register_cpugear(struct device *dev,
  62. struct regmap *regmap,
  63. const char *name,
  64. const struct uniphier_clk_cpugear_data *data)
  65. {
  66. struct uniphier_clk_cpugear *gear;
  67. struct clk_init_data init;
  68. int ret;
  69. gear = devm_kzalloc(dev, sizeof(*gear), GFP_KERNEL);
  70. if (!gear)
  71. return ERR_PTR(-ENOMEM);
  72. init.name = name;
  73. init.ops = &uniphier_clk_cpugear_ops;
  74. init.flags = CLK_SET_RATE_PARENT;
  75. init.parent_names = data->parent_names;
  76. init.num_parents = data->num_parents;
  77. gear->regmap = regmap;
  78. gear->regbase = data->regbase;
  79. gear->mask = data->mask;
  80. gear->hw.init = &init;
  81. ret = devm_clk_hw_register(dev, &gear->hw);
  82. if (ret)
  83. return ERR_PTR(ret);
  84. return &gear->hw;
  85. }