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