Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * Copyright 2018 NXP
  4 *	Dong Aisheng <aisheng.dong@nxp.com>
  5 */
  6
  7#include <linux/bits.h>
  8#include <linux/clk-provider.h>
  9#include <linux/err.h>
 10#include <linux/io.h>
 11#include <linux/slab.h>
 12#include <linux/spinlock.h>
 13
 14#include "clk-scu.h"
 15
 16static DEFINE_SPINLOCK(imx_lpcg_scu_lock);
 17
 18#define CLK_GATE_SCU_LPCG_MASK		0x3
 19#define CLK_GATE_SCU_LPCG_HW_SEL	BIT(0)
 20#define CLK_GATE_SCU_LPCG_SW_SEL	BIT(1)
 21
 22/*
 23 * struct clk_lpcg_scu - Description of LPCG clock
 24 *
 25 * @hw: clk_hw of this LPCG
 26 * @reg: register of this LPCG clock
 27 * @bit_idx: bit index of this LPCG clock
 28 * @hw_gate: HW auto gate enable
 29 *
 30 * This structure describes one LPCG clock
 31 */
 32struct clk_lpcg_scu {
 33	struct clk_hw hw;
 34	void __iomem *reg;
 35	u8 bit_idx;
 36	bool hw_gate;
 37
 38	/* for state save&restore */
 39	u32 state;
 40};
 41
 42#define to_clk_lpcg_scu(_hw) container_of(_hw, struct clk_lpcg_scu, hw)
 43
 44static int clk_lpcg_scu_enable(struct clk_hw *hw)
 45{
 46	struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw);
 47	unsigned long flags;
 48	u32 reg, val;
 49
 50	spin_lock_irqsave(&imx_lpcg_scu_lock, flags);
 51
 52	reg = readl_relaxed(clk->reg);
 53	reg &= ~(CLK_GATE_SCU_LPCG_MASK << clk->bit_idx);
 54
 55	val = CLK_GATE_SCU_LPCG_SW_SEL;
 56	if (clk->hw_gate)
 57		val |= CLK_GATE_SCU_LPCG_HW_SEL;
 58
 59	reg |= val << clk->bit_idx;
 60	writel(reg, clk->reg);
 61
 62	spin_unlock_irqrestore(&imx_lpcg_scu_lock, flags);
 63
 64	return 0;
 65}
 66
 67static void clk_lpcg_scu_disable(struct clk_hw *hw)
 68{
 69	struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw);
 70	unsigned long flags;
 71	u32 reg;
 72
 73	spin_lock_irqsave(&imx_lpcg_scu_lock, flags);
 74
 75	reg = readl_relaxed(clk->reg);
 76	reg &= ~(CLK_GATE_SCU_LPCG_MASK << clk->bit_idx);
 77	writel(reg, clk->reg);
 78
 79	spin_unlock_irqrestore(&imx_lpcg_scu_lock, flags);
 80}
 81
 82static const struct clk_ops clk_lpcg_scu_ops = {
 83	.enable = clk_lpcg_scu_enable,
 84	.disable = clk_lpcg_scu_disable,
 85};
 86
 87struct clk_hw *__imx_clk_lpcg_scu(struct device *dev, const char *name,
 88				  const char *parent_name, unsigned long flags,
 89				  void __iomem *reg, u8 bit_idx, bool hw_gate)
 90{
 91	struct clk_lpcg_scu *clk;
 92	struct clk_init_data init;
 93	struct clk_hw *hw;
 94	int ret;
 95
 96	clk = kzalloc(sizeof(*clk), GFP_KERNEL);
 97	if (!clk)
 98		return ERR_PTR(-ENOMEM);
 99
100	clk->reg = reg;
101	clk->bit_idx = bit_idx;
102	clk->hw_gate = hw_gate;
103
104	init.name = name;
105	init.ops = &clk_lpcg_scu_ops;
106	init.flags = CLK_SET_RATE_PARENT | flags;
107	init.parent_names = parent_name ? &parent_name : NULL;
108	init.num_parents = parent_name ? 1 : 0;
109
110	clk->hw.init = &init;
111
112	hw = &clk->hw;
113	ret = clk_hw_register(dev, hw);
114	if (ret) {
115		kfree(clk);
116		hw = ERR_PTR(ret);
117		return hw;
118	}
119
120	if (dev)
121		dev_set_drvdata(dev, clk);
122
123	return hw;
124}
125
126void imx_clk_lpcg_scu_unregister(struct clk_hw *hw)
127{
128	struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw);
129
130	clk_hw_unregister(&clk->hw);
131	kfree(clk);
132}
133
134static int __maybe_unused imx_clk_lpcg_scu_suspend(struct device *dev)
135{
136	struct clk_lpcg_scu *clk = dev_get_drvdata(dev);
137
138	clk->state = readl_relaxed(clk->reg);
139	dev_dbg(dev, "save lpcg state 0x%x\n", clk->state);
140
141	return 0;
142}
143
144static int __maybe_unused imx_clk_lpcg_scu_resume(struct device *dev)
145{
146	struct clk_lpcg_scu *clk = dev_get_drvdata(dev);
147
148	/*
149	 * FIXME: Sometimes writes don't work unless the CPU issues
150	 * them twice
151	 */
152
153	writel(clk->state, clk->reg);
154	writel(clk->state, clk->reg);
155	dev_dbg(dev, "restore lpcg state 0x%x\n", clk->state);
156
157	return 0;
158}
159
160const struct dev_pm_ops imx_clk_lpcg_scu_pm_ops = {
161	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_clk_lpcg_scu_suspend,
162				      imx_clk_lpcg_scu_resume)
163};