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