clk-uniphier-cpugear.c 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  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/delay.h>
  17. #include <linux/device.h>
  18. #include <linux/regmap.h>
  19. #include "clk-uniphier.h"
  20. #define UNIPHIER_CLK_CPUGEAR_STAT 0 /* status */
  21. #define UNIPHIER_CLK_CPUGEAR_SET 4 /* set */
  22. #define UNIPHIER_CLK_CPUGEAR_UPD 8 /* update */
  23. #define UNIPHIER_CLK_CPUGEAR_UPD_BIT BIT(0)
  24. struct uniphier_clk_cpugear {
  25. struct clk_hw hw;
  26. struct regmap *regmap;
  27. unsigned int regbase;
  28. unsigned int mask;
  29. };
  30. #define to_uniphier_clk_cpugear(_hw) \
  31. container_of(_hw, struct uniphier_clk_cpugear, hw)
  32. static int uniphier_clk_cpugear_set_parent(struct clk_hw *hw, u8 index)
  33. {
  34. struct uniphier_clk_cpugear *gear = to_uniphier_clk_cpugear(hw);
  35. int ret;
  36. unsigned int val;
  37. ret = regmap_write_bits(gear->regmap,
  38. gear->regbase + UNIPHIER_CLK_CPUGEAR_SET,
  39. gear->mask, index);
  40. if (ret)
  41. return ret;
  42. ret = regmap_write_bits(gear->regmap,
  43. gear->regbase + UNIPHIER_CLK_CPUGEAR_SET,
  44. UNIPHIER_CLK_CPUGEAR_UPD_BIT,
  45. UNIPHIER_CLK_CPUGEAR_UPD_BIT);
  46. if (ret)
  47. return ret;
  48. return regmap_read_poll_timeout(gear->regmap,
  49. gear->regbase + UNIPHIER_CLK_CPUGEAR_UPD,
  50. val, !(val & UNIPHIER_CLK_CPUGEAR_UPD_BIT),
  51. 0, 1);
  52. }
  53. static u8 uniphier_clk_cpugear_get_parent(struct clk_hw *hw)
  54. {
  55. struct uniphier_clk_cpugear *gear = to_uniphier_clk_cpugear(hw);
  56. int num_parents = clk_hw_get_num_parents(hw);
  57. int ret;
  58. unsigned int val;
  59. ret = regmap_read(gear->regmap,
  60. gear->regbase + UNIPHIER_CLK_CPUGEAR_STAT, &val);
  61. if (ret)
  62. return ret;
  63. val &= gear->mask;
  64. return val < num_parents ? val : -EINVAL;
  65. }
  66. static const struct clk_ops uniphier_clk_cpugear_ops = {
  67. .determine_rate = __clk_mux_determine_rate,
  68. .set_parent = uniphier_clk_cpugear_set_parent,
  69. .get_parent = uniphier_clk_cpugear_get_parent,
  70. };
  71. struct clk_hw *uniphier_clk_register_cpugear(struct device *dev,
  72. struct regmap *regmap,
  73. const char *name,
  74. const struct uniphier_clk_cpugear_data *data)
  75. {
  76. struct uniphier_clk_cpugear *gear;
  77. struct clk_init_data init;
  78. int ret;
  79. gear = devm_kzalloc(dev, sizeof(*gear), GFP_KERNEL);
  80. if (!gear)
  81. return ERR_PTR(-ENOMEM);
  82. init.name = name;
  83. init.ops = &uniphier_clk_cpugear_ops;
  84. init.flags = CLK_SET_RATE_PARENT;
  85. init.parent_names = data->parent_names;
  86. init.num_parents = data->num_parents,
  87. gear->regmap = regmap;
  88. gear->regbase = data->regbase;
  89. gear->mask = data->mask;
  90. gear->hw.init = &init;
  91. ret = devm_clk_hw_register(dev, &gear->hw);
  92. if (ret)
  93. return ERR_PTR(ret);
  94. return &gear->hw;
  95. }