1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright 2019 NXP
4 */
5
6#include <common.h>
7#include <log.h>
8#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/clk-provider.h>
14#include <clk.h>
15#include "clk.h"
16#include <linux/err.h>
17
18#define UBOOT_DM_CLK_IMX_COMPOSITE "imx_clk_composite"
19
20#define PCG_PREDIV_SHIFT	16
21#define PCG_PREDIV_WIDTH	3
22#define PCG_PREDIV_MAX		8
23
24#define PCG_DIV_SHIFT		0
25#define PCG_DIV_WIDTH		6
26#define PCG_DIV_MAX		64
27
28#define PCG_PCS_SHIFT		24
29#define PCG_PCS_MASK		0x7
30
31#define PCG_CGC_SHIFT		28
32
33static unsigned long imx8m_clk_composite_divider_recalc_rate(struct clk *clk)
34{
35	struct clk_divider *divider = (struct clk_divider *)to_clk_divider(clk);
36	struct clk_composite *composite = (struct clk_composite *)clk->data;
37	ulong parent_rate = clk_get_parent_rate(&composite->clk);
38	unsigned long prediv_rate;
39	unsigned int prediv_value;
40	unsigned int div_value;
41
42	debug("%s: name %s prate: %lu reg: %p\n", __func__,
43	      (&composite->clk)->dev->name, parent_rate, divider->reg);
44	prediv_value = readl(divider->reg) >> divider->shift;
45	prediv_value &= clk_div_mask(divider->width);
46
47	prediv_rate = divider_recalc_rate(clk, parent_rate, prediv_value,
48					  NULL, divider->flags,
49					  divider->width);
50
51	div_value = readl(divider->reg) >> PCG_DIV_SHIFT;
52	div_value &= clk_div_mask(PCG_DIV_WIDTH);
53
54	return divider_recalc_rate(clk, prediv_rate, div_value, NULL,
55				   divider->flags, PCG_DIV_WIDTH);
56}
57
58static int imx8m_clk_composite_compute_dividers(unsigned long rate,
59						unsigned long parent_rate,
60						int *prediv, int *postdiv)
61{
62	int div1, div2;
63	int error = INT_MAX;
64	int ret = -EINVAL;
65
66	*prediv = 1;
67	*postdiv = 1;
68
69	for (div1 = 1; div1 <= PCG_PREDIV_MAX; div1++) {
70		for (div2 = 1; div2 <= PCG_DIV_MAX; div2++) {
71			int new_error = ((parent_rate / div1) / div2) - rate;
72
73			if (abs(new_error) < abs(error)) {
74				*prediv = div1;
75				*postdiv = div2;
76				error = new_error;
77				ret = 0;
78			}
79		}
80	}
81	return ret;
82}
83
84/*
85 * The clk are bound to a dev, because it is part of composite clk
86 * use composite clk to get dev
87 */
88static ulong imx8m_clk_composite_divider_set_rate(struct clk *clk,
89						  unsigned long rate)
90{
91	struct clk_divider *divider = (struct clk_divider *)to_clk_divider(clk);
92	struct clk_composite *composite = (struct clk_composite *)clk->data;
93	ulong parent_rate = clk_get_parent_rate(&composite->clk);
94	int prediv_value;
95	int div_value;
96	int ret;
97	u32 val;
98
99	ret = imx8m_clk_composite_compute_dividers(rate, parent_rate,
100						   &prediv_value, &div_value);
101	if (ret)
102		return ret;
103
104	val = readl(divider->reg);
105	val &= ~((clk_div_mask(divider->width) << divider->shift) |
106			(clk_div_mask(PCG_DIV_WIDTH) << PCG_DIV_SHIFT));
107
108	val |= (u32)(prediv_value  - 1) << divider->shift;
109	val |= (u32)(div_value - 1) << PCG_DIV_SHIFT;
110	writel(val, divider->reg);
111
112	return clk_get_rate(&composite->clk);
113}
114
115static const struct clk_ops imx8m_clk_composite_divider_ops = {
116	.get_rate = imx8m_clk_composite_divider_recalc_rate,
117	.set_rate = imx8m_clk_composite_divider_set_rate,
118};
119
120struct clk *imx8m_clk_composite_flags(const char *name,
121				      const char * const *parent_names,
122				      int num_parents, void __iomem *reg,
123				      unsigned long flags)
124{
125	struct clk *clk = ERR_PTR(-ENOMEM);
126	struct clk_divider *div = NULL;
127	struct clk_gate *gate = NULL;
128	struct clk_mux *mux = NULL;
129
130	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
131	if (!mux)
132		goto fail;
133
134	mux->reg = reg;
135	mux->shift = PCG_PCS_SHIFT;
136	mux->mask = PCG_PCS_MASK;
137	mux->num_parents = num_parents;
138	mux->flags = flags;
139	mux->parent_names = parent_names;
140
141	div = kzalloc(sizeof(*div), GFP_KERNEL);
142	if (!div)
143		goto fail;
144
145	div->reg = reg;
146	div->shift = PCG_PREDIV_SHIFT;
147	div->width = PCG_PREDIV_WIDTH;
148	div->flags = CLK_DIVIDER_ROUND_CLOSEST | flags;
149
150	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
151	if (!gate)
152		goto fail;
153
154	gate->reg = reg;
155	gate->bit_idx = PCG_CGC_SHIFT;
156	gate->flags = flags;
157
158	clk = clk_register_composite(NULL, name,
159				     parent_names, num_parents,
160				     &mux->clk, &clk_mux_ops, &div->clk,
161				     &imx8m_clk_composite_divider_ops,
162				     &gate->clk, &clk_gate_ops, flags);
163	if (IS_ERR(clk))
164		goto fail;
165
166	return clk;
167
168fail:
169	kfree(gate);
170	kfree(div);
171	kfree(mux);
172	return ERR_CAST(clk);
173}
174