blob: d7f2640fbb7c25b0b1ecd50bbf6a5ed23191b0e6 [file] [log] [blame]
Sébastien Szymanski8d163f52023-07-25 10:08:53 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright 2022 NXP
4 *
5 * Peng Fan <peng.fan@nxp.com>
6 */
7
Sébastien Szymanski8d163f52023-07-25 10:08:53 +02008#include <asm/io.h>
9#include <malloc.h>
10#include <clk-uclass.h>
11#include <dm/device.h>
12#include <dm/devres.h>
13#include <linux/bug.h>
14#include <linux/clk-provider.h>
15#include <clk.h>
16#include "clk.h"
17#include <linux/err.h>
18
19#define UBOOT_DM_CLK_IMX_GATE93 "imx_clk_gate93"
20
21#define DIRECT_OFFSET 0x0
22
23/*
24 * 0b000 - LPCG will be OFF in any CPU mode.
25 * 0b100 - LPCG will be ON in any CPU mode.
26 */
27#define LPM_SETTING_OFF 0x0
28#define LPM_SETTING_ON 0x4
29
30#define LPM_CUR_OFFSET 0x1c
31
32#define AUTHEN_OFFSET 0x30
33#define CPULPM_EN BIT(2)
34#define TZ_NS_SHIFT 9
35#define TZ_NS_MASK BIT(9)
36
37#define WHITE_LIST_SHIFT 16
38
39struct imx93_clk_gate {
40 struct clk clk;
41 void __iomem *reg;
42 u32 bit_idx;
43 u32 val;
44 u32 mask;
45 unsigned int *share_count;
46};
47
48#define to_imx93_clk_gate(_clk) container_of(_clk, struct imx93_clk_gate, clk)
49
50static void imx93_clk_gate_do_hardware(struct clk *clk, bool enable)
51{
52 struct imx93_clk_gate *gate = to_imx93_clk_gate(clk);
53 u32 val;
54
55 val = readl(gate->reg + AUTHEN_OFFSET);
56 if (val & CPULPM_EN) {
57 val = enable ? LPM_SETTING_ON : LPM_SETTING_OFF;
58 writel(val, gate->reg + LPM_CUR_OFFSET);
59 } else {
60 val = readl(gate->reg + DIRECT_OFFSET);
61 val &= ~(gate->mask << gate->bit_idx);
62 if (enable)
63 val |= (gate->val & gate->mask) << gate->bit_idx;
64 writel(val, gate->reg + DIRECT_OFFSET);
65 }
66}
67
68static int imx93_clk_gate_enable(struct clk *clk)
69{
70 struct imx93_clk_gate *gate = to_imx93_clk_gate(clk);
71
72 if (gate->share_count && (*gate->share_count)++ > 0)
73 return 0;
74
75 imx93_clk_gate_do_hardware(clk, true);
76
77 return 0;
78}
79
80static int imx93_clk_gate_disable(struct clk *clk)
81{
82 struct imx93_clk_gate *gate = to_imx93_clk_gate(clk);
83
84 if (gate->share_count) {
85 if (WARN_ON(*gate->share_count == 0))
86 return 0;
87 else if (--(*gate->share_count) > 0)
88 return 0;
89 }
90
91 imx93_clk_gate_do_hardware(clk, false);
92
93 return 0;
94}
95
96static ulong imx93_clk_set_rate(struct clk *clk, ulong rate)
97{
98 struct clk *parent = clk_get_parent(clk);
99
100 if (parent)
101 return clk_set_rate(parent, rate);
102
103 return -ENODEV;
104}
105
106static const struct clk_ops imx93_clk_gate_ops = {
107 .enable = imx93_clk_gate_enable,
108 .disable = imx93_clk_gate_disable,
109 .get_rate = clk_generic_get_rate,
110 .set_rate = imx93_clk_set_rate,
111};
112
113struct clk *imx93_clk_gate(struct device *dev, const char *name, const char *parent_name,
114 unsigned long flags, void __iomem *reg, u32 bit_idx, u32 val,
115 u32 mask, u32 domain_id, unsigned int *share_count)
116{
117 struct imx93_clk_gate *gate;
118 struct clk *clk;
119 int ret;
120
121 gate = kzalloc(sizeof(*gate), GFP_KERNEL);
122 if (!gate)
123 return ERR_PTR(-ENOMEM);
124
125 gate->reg = reg;
126 gate->bit_idx = bit_idx;
127 gate->val = val;
128 gate->mask = mask;
129 gate->share_count = share_count;
130
131 clk = &gate->clk;
132
133 ret = clk_register(clk, UBOOT_DM_CLK_IMX_GATE93, name, parent_name);
134 if (ret) {
135 kfree(gate);
136 return ERR_PTR(ret);
137 }
138
139 return clk;
140}
141
142U_BOOT_DRIVER(clk_gate93) = {
143 .name = UBOOT_DM_CLK_IMX_GATE93,
144 .id = UCLASS_CLK,
145 .ops = &imx93_clk_gate_ops,
146 .flags = DM_FLAG_PRE_RELOC,
147};