clk-inverter.c 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Copyright 2015 Heiko Stuebner <[email protected]>
  4. */
  5. #include <linux/slab.h>
  6. #include <linux/clk-provider.h>
  7. #include <linux/io.h>
  8. #include <linux/spinlock.h>
  9. #include <linux/kernel.h>
  10. #include "clk.h"
  11. struct rockchip_inv_clock {
  12. struct clk_hw hw;
  13. void __iomem *reg;
  14. int shift;
  15. int flags;
  16. spinlock_t *lock;
  17. };
  18. #define to_inv_clock(_hw) container_of(_hw, struct rockchip_inv_clock, hw)
  19. #define INVERTER_MASK 0x1
  20. static int rockchip_inv_get_phase(struct clk_hw *hw)
  21. {
  22. struct rockchip_inv_clock *inv_clock = to_inv_clock(hw);
  23. u32 val;
  24. val = readl(inv_clock->reg) >> inv_clock->shift;
  25. val &= INVERTER_MASK;
  26. return val ? 180 : 0;
  27. }
  28. static int rockchip_inv_set_phase(struct clk_hw *hw, int degrees)
  29. {
  30. struct rockchip_inv_clock *inv_clock = to_inv_clock(hw);
  31. u32 val;
  32. if (degrees % 180 == 0) {
  33. val = !!degrees;
  34. } else {
  35. pr_err("%s: unsupported phase %d for %s\n",
  36. __func__, degrees, clk_hw_get_name(hw));
  37. return -EINVAL;
  38. }
  39. if (inv_clock->flags & ROCKCHIP_INVERTER_HIWORD_MASK) {
  40. writel(HIWORD_UPDATE(val, INVERTER_MASK, inv_clock->shift),
  41. inv_clock->reg);
  42. } else {
  43. unsigned long flags;
  44. u32 reg;
  45. spin_lock_irqsave(inv_clock->lock, flags);
  46. reg = readl(inv_clock->reg);
  47. reg &= ~BIT(inv_clock->shift);
  48. reg |= val;
  49. writel(reg, inv_clock->reg);
  50. spin_unlock_irqrestore(inv_clock->lock, flags);
  51. }
  52. return 0;
  53. }
  54. static const struct clk_ops rockchip_inv_clk_ops = {
  55. .get_phase = rockchip_inv_get_phase,
  56. .set_phase = rockchip_inv_set_phase,
  57. };
  58. struct clk *rockchip_clk_register_inverter(const char *name,
  59. const char *const *parent_names, u8 num_parents,
  60. void __iomem *reg, int shift, int flags,
  61. spinlock_t *lock)
  62. {
  63. struct clk_init_data init;
  64. struct rockchip_inv_clock *inv_clock;
  65. struct clk *clk;
  66. inv_clock = kmalloc(sizeof(*inv_clock), GFP_KERNEL);
  67. if (!inv_clock)
  68. return ERR_PTR(-ENOMEM);
  69. init.name = name;
  70. init.num_parents = num_parents;
  71. init.flags = CLK_SET_RATE_PARENT;
  72. init.parent_names = parent_names;
  73. init.ops = &rockchip_inv_clk_ops;
  74. inv_clock->hw.init = &init;
  75. inv_clock->reg = reg;
  76. inv_clock->shift = shift;
  77. inv_clock->flags = flags;
  78. inv_clock->lock = lock;
  79. clk = clk_register(NULL, &inv_clock->hw);
  80. if (IS_ERR(clk))
  81. kfree(inv_clock);
  82. return clk;
  83. }