1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
211f68120SShawn Guo /*
311f68120SShawn Guo * Copyright 2014 Freescale Semiconductor, Inc.
411f68120SShawn Guo */
511f68120SShawn Guo
611f68120SShawn Guo #include <linux/clk-provider.h>
711f68120SShawn Guo #include <linux/err.h>
811f68120SShawn Guo #include <linux/io.h>
911f68120SShawn Guo #include <linux/slab.h>
1011f68120SShawn Guo #include "clk.h"
1111f68120SShawn Guo
1211f68120SShawn Guo /**
1311f68120SShawn Guo * struct clk_gate_exclusive - i.MX specific gate clock which is mutually
1411f68120SShawn Guo * exclusive with other gate clocks
1511f68120SShawn Guo *
1611f68120SShawn Guo * @gate: the parent class
1711f68120SShawn Guo * @exclusive_mask: mask of gate bits which are mutually exclusive to this
1811f68120SShawn Guo * gate clock
1911f68120SShawn Guo *
2011f68120SShawn Guo * The imx exclusive gate clock is a subclass of basic clk_gate
2111f68120SShawn Guo * with an addtional mask to indicate which other gate bits in the same
2211f68120SShawn Guo * register is mutually exclusive to this gate clock.
2311f68120SShawn Guo */
2411f68120SShawn Guo struct clk_gate_exclusive {
2511f68120SShawn Guo struct clk_gate gate;
2611f68120SShawn Guo u32 exclusive_mask;
2711f68120SShawn Guo };
2811f68120SShawn Guo
clk_gate_exclusive_enable(struct clk_hw * hw)2911f68120SShawn Guo static int clk_gate_exclusive_enable(struct clk_hw *hw)
3011f68120SShawn Guo {
315fd9c05cSGeliang Tang struct clk_gate *gate = to_clk_gate(hw);
3211f68120SShawn Guo struct clk_gate_exclusive *exgate = container_of(gate,
3311f68120SShawn Guo struct clk_gate_exclusive, gate);
3411f68120SShawn Guo u32 val = readl(gate->reg);
3511f68120SShawn Guo
3611f68120SShawn Guo if (val & exgate->exclusive_mask)
3711f68120SShawn Guo return -EBUSY;
3811f68120SShawn Guo
3911f68120SShawn Guo return clk_gate_ops.enable(hw);
4011f68120SShawn Guo }
4111f68120SShawn Guo
clk_gate_exclusive_disable(struct clk_hw * hw)4211f68120SShawn Guo static void clk_gate_exclusive_disable(struct clk_hw *hw)
4311f68120SShawn Guo {
4411f68120SShawn Guo clk_gate_ops.disable(hw);
4511f68120SShawn Guo }
4611f68120SShawn Guo
clk_gate_exclusive_is_enabled(struct clk_hw * hw)4711f68120SShawn Guo static int clk_gate_exclusive_is_enabled(struct clk_hw *hw)
4811f68120SShawn Guo {
4911f68120SShawn Guo return clk_gate_ops.is_enabled(hw);
5011f68120SShawn Guo }
5111f68120SShawn Guo
5211f68120SShawn Guo static const struct clk_ops clk_gate_exclusive_ops = {
5311f68120SShawn Guo .enable = clk_gate_exclusive_enable,
5411f68120SShawn Guo .disable = clk_gate_exclusive_disable,
5511f68120SShawn Guo .is_enabled = clk_gate_exclusive_is_enabled,
5611f68120SShawn Guo };
5711f68120SShawn Guo
imx_clk_hw_gate_exclusive(const char * name,const char * parent,void __iomem * reg,u8 shift,u32 exclusive_mask)58dfc148b3SAbel Vesa struct clk_hw *imx_clk_hw_gate_exclusive(const char *name, const char *parent,
5911f68120SShawn Guo void __iomem *reg, u8 shift, u32 exclusive_mask)
6011f68120SShawn Guo {
6111f68120SShawn Guo struct clk_gate_exclusive *exgate;
6211f68120SShawn Guo struct clk_gate *gate;
63dfc148b3SAbel Vesa struct clk_hw *hw;
6411f68120SShawn Guo struct clk_init_data init;
65dfc148b3SAbel Vesa int ret;
6611f68120SShawn Guo
6711f68120SShawn Guo if (exclusive_mask == 0)
6811f68120SShawn Guo return ERR_PTR(-EINVAL);
6911f68120SShawn Guo
7011f68120SShawn Guo exgate = kzalloc(sizeof(*exgate), GFP_KERNEL);
7111f68120SShawn Guo if (!exgate)
7211f68120SShawn Guo return ERR_PTR(-ENOMEM);
7311f68120SShawn Guo gate = &exgate->gate;
7411f68120SShawn Guo
7511f68120SShawn Guo init.name = name;
7611f68120SShawn Guo init.ops = &clk_gate_exclusive_ops;
7711f68120SShawn Guo init.flags = CLK_SET_RATE_PARENT;
7811f68120SShawn Guo init.parent_names = parent ? &parent : NULL;
7911f68120SShawn Guo init.num_parents = parent ? 1 : 0;
8011f68120SShawn Guo
8111f68120SShawn Guo gate->reg = reg;
8211f68120SShawn Guo gate->bit_idx = shift;
8311f68120SShawn Guo gate->lock = &imx_ccm_lock;
8411f68120SShawn Guo gate->hw.init = &init;
8511f68120SShawn Guo exgate->exclusive_mask = exclusive_mask;
8611f68120SShawn Guo
87dfc148b3SAbel Vesa hw = &gate->hw;
8811f68120SShawn Guo
89dfc148b3SAbel Vesa ret = clk_hw_register(NULL, hw);
90dfc148b3SAbel Vesa if (ret) {
91dfc148b3SAbel Vesa kfree(gate);
92dfc148b3SAbel Vesa return ERR_PTR(ret);
93dfc148b3SAbel Vesa }
94dfc148b3SAbel Vesa
95dfc148b3SAbel Vesa return hw;
9611f68120SShawn Guo }
97