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};
164