1 // SPDX-License-Identifier: GPL-2.0+ or BSD-3-Clause
2 /*
3  *  Copyright (C) 2015 - 2021 Atmel Corporation,
4  *                            Nicolas Ferre <nicolas.ferre@atmel.com>
5  *
6  * Based on clk-programmable & clk-peripheral drivers by Boris BREZILLON.
7  */
8 
9 #include <io.h>
10 #include <kernel/delay.h>
11 #include <kernel/panic.h>
12 #include <mm/core_memprot.h>
13 #include <string.h>
14 #include <types_ext.h>
15 
16 #include "at91_clk.h"
17 
18 #define GENERATED_MAX_DIV	255
19 
20 struct clk_generated {
21 	vaddr_t base;
22 	struct clk_range range;
23 	uint32_t *mux_table;
24 	uint32_t id;
25 	uint32_t gckdiv;
26 	const struct clk_pcr_layout *layout;
27 	uint8_t parent_id;
28 	int chg_pid;
29 };
30 
clk_generated_enable(struct clk * clk)31 static TEE_Result clk_generated_enable(struct clk *clk)
32 {
33 	struct clk_generated *gck = clk->priv;
34 
35 	io_write32(gck->base + gck->layout->offset,
36 		   (gck->id & gck->layout->pid_mask));
37 	io_clrsetbits32(gck->base + gck->layout->offset,
38 			AT91_PMC_PCR_GCKDIV_MASK | gck->layout->gckcss_mask |
39 			gck->layout->cmd | AT91_PMC_PCR_GCKEN,
40 			field_prep(gck->layout->gckcss_mask, gck->parent_id) |
41 			gck->layout->cmd |
42 			((gck->gckdiv << AT91_PMC_PCR_GCKDIV_SHIFT) &
43 			 AT91_PMC_PCR_GCKDIV_MASK) |
44 			AT91_PMC_PCR_GCKEN);
45 
46 	return TEE_SUCCESS;
47 }
48 
clk_generated_disable(struct clk * clk)49 static void clk_generated_disable(struct clk *clk)
50 {
51 	struct clk_generated *gck = clk->priv;
52 
53 	io_write32(gck->base + gck->layout->offset,
54 		   gck->id & gck->layout->pid_mask);
55 	io_clrsetbits32(gck->base + gck->layout->offset, AT91_PMC_PCR_GCKEN,
56 			gck->layout->cmd);
57 }
58 
59 static unsigned long
clk_generated_get_rate(struct clk * clk,unsigned long parent_rate)60 clk_generated_get_rate(struct clk *clk, unsigned long parent_rate)
61 {
62 	struct clk_generated *gck = clk->priv;
63 
64 	return UDIV_ROUND_NEAREST(parent_rate, gck->gckdiv + 1);
65 }
66 
67 /* No modification of hardware as we have the flag CLK_SET_PARENT_GATE set */
clk_generated_set_parent(struct clk * clk,size_t index)68 static TEE_Result clk_generated_set_parent(struct clk *clk, size_t index)
69 {
70 	struct clk_generated *gck = clk->priv;
71 
72 	if (index >= clk_get_num_parents(clk))
73 		return TEE_ERROR_BAD_PARAMETERS;
74 
75 	gck->parent_id = index;
76 
77 	return TEE_SUCCESS;
78 }
79 
clk_generated_get_parent(struct clk * clk)80 static size_t clk_generated_get_parent(struct clk *clk)
81 {
82 	struct clk_generated *gck = clk->priv;
83 
84 	return gck->parent_id;
85 }
86 
87 /* No modification of hardware as we have the flag CLK_SET_RATE_GATE set */
clk_generated_set_rate(struct clk * clk,unsigned long rate,unsigned long parent_rate)88 static TEE_Result clk_generated_set_rate(struct clk *clk, unsigned long rate,
89 					 unsigned long parent_rate)
90 {
91 	struct clk_generated *gck = clk->priv;
92 	uint32_t div = 1;
93 
94 	if (!rate)
95 		return TEE_ERROR_BAD_PARAMETERS;
96 
97 	if (gck->range.max && rate > gck->range.max)
98 		return TEE_ERROR_BAD_PARAMETERS;
99 
100 	div = UDIV_ROUND_NEAREST(parent_rate, rate);
101 	if (div > GENERATED_MAX_DIV + 1 || !div)
102 		return TEE_ERROR_GENERIC;
103 
104 	gck->gckdiv = div - 1;
105 	return TEE_SUCCESS;
106 }
107 
108 static const struct clk_ops generated_ops = {
109 	.enable = clk_generated_enable,
110 	.disable = clk_generated_disable,
111 	.get_rate = clk_generated_get_rate,
112 	.get_parent = clk_generated_get_parent,
113 	.set_parent = clk_generated_set_parent,
114 	.set_rate = clk_generated_set_rate,
115 };
116 
117 /**
118  * clk_generated_startup - Initialize a given clock to its default parent and
119  * divisor parameter.
120  *
121  * @gck:	Generated clock to set the startup parameters for.
122  *
123  * Take parameters from the hardware and update local clock configuration
124  * accordingly.
125  */
clk_generated_startup(struct clk_generated * gck)126 static void clk_generated_startup(struct clk_generated *gck)
127 {
128 	uint32_t tmp = 0;
129 
130 	io_write32(gck->base + gck->layout->offset,
131 		   (gck->id & gck->layout->pid_mask));
132 	tmp = io_read32(gck->base + gck->layout->offset);
133 
134 	gck->parent_id = field_get(gck->layout->gckcss_mask, tmp);
135 	gck->gckdiv = (tmp & AT91_PMC_PCR_GCKDIV_MASK) >>
136 		      AT91_PMC_PCR_GCKDIV_SHIFT;
137 }
138 
139 struct clk *
at91_clk_register_generated(struct pmc_data * pmc,const struct clk_pcr_layout * layout,const char * name,struct clk ** parents,uint8_t num_parents,uint8_t id,const struct clk_range * range,int chg_pid)140 at91_clk_register_generated(struct pmc_data *pmc,
141 			    const struct clk_pcr_layout *layout,
142 			    const char *name, struct clk **parents,
143 			    uint8_t num_parents, uint8_t id,
144 			    const struct clk_range *range,
145 			    int chg_pid)
146 {
147 	struct clk_generated *gck = NULL;
148 	struct clk *clk = NULL;
149 
150 	clk = clk_alloc(name, &generated_ops, parents, num_parents);
151 	if (!clk)
152 		return NULL;
153 
154 	gck = calloc(1, sizeof(*gck));
155 	if (!gck) {
156 		clk_free(clk);
157 		return NULL;
158 	}
159 
160 	clk->flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
161 
162 	gck->id = id;
163 	gck->base = pmc->base;
164 	memcpy(&gck->range, range, sizeof(gck->range));
165 	gck->chg_pid = chg_pid;
166 	gck->layout = layout;
167 
168 	clk->priv = gck;
169 
170 	clk_generated_startup(gck);
171 
172 	if (clk_register(clk)) {
173 		clk_free(clk);
174 		free(gck);
175 		return NULL;
176 	}
177 	pmc_register_id(id);
178 
179 	return clk;
180 }
181