1 // SPDX-License-Identifier: GPL-2.0+ or BSD-3-Clause
2 /*
3 * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
4 * Copyright (C) 2021 Microchip
5 */
6
7 #include <io.h>
8 #include <kernel/delay.h>
9 #include <mm/core_memprot.h>
10 #include <types_ext.h>
11
12 #include "at91_clk.h"
13
14 #define MASTER_PRES_MASK 0x7
15 #define MASTER_PRES_MAX MASTER_PRES_MASK
16 #define MASTER_DIV_SHIFT 8
17 #define MASTER_DIV_MASK 0x7
18
19 struct clk_master {
20 vaddr_t base;
21 const struct clk_master_layout *layout;
22 const struct clk_master_charac *charac;
23 uint32_t *mux_table;
24 uint32_t mckr;
25 int chg_pid;
26 uint8_t div;
27 };
28
clk_master_ready(struct clk_master * master)29 static bool clk_master_ready(struct clk_master *master)
30 {
31 uint32_t status = io_read32(master->base + AT91_PMC_SR);
32
33 return status & AT91_PMC_MCKRDY;
34 }
35
clk_master_enable(struct clk * clk)36 static TEE_Result clk_master_enable(struct clk *clk)
37 {
38 struct clk_master *master = clk->priv;
39
40 while (!clk_master_ready(master))
41 ;
42
43 return TEE_SUCCESS;
44 }
45
clk_master_div_get_rate(struct clk * clk,unsigned long parent_rate)46 static unsigned long clk_master_div_get_rate(struct clk *clk,
47 unsigned long parent_rate)
48 {
49 uint8_t div = 1;
50 uint32_t mckr = 0;
51 unsigned long rate = parent_rate;
52 struct clk_master *master = clk->priv;
53 const struct clk_master_layout *layout = master->layout;
54 const struct clk_master_charac *charac = master->charac;
55
56 mckr = io_read32(master->base + master->layout->offset);
57
58 mckr &= layout->mask;
59
60 div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
61
62 rate /= charac->divisors[div];
63
64 if (rate < charac->output.min)
65 IMSG("master clk div is underclocked");
66 else if (rate > charac->output.max)
67 IMSG("master clk div is overclocked");
68
69 return rate;
70 }
71
72 static const struct clk_ops master_div_ops = {
73 .enable = clk_master_enable,
74 .get_rate = clk_master_div_get_rate,
75 };
76
clk_master_pres_get_rate(struct clk * clk,unsigned long parent_rate)77 static unsigned long clk_master_pres_get_rate(struct clk *clk,
78 unsigned long parent_rate)
79 {
80 struct clk_master *master = clk->priv;
81 const struct clk_master_charac *charac = master->charac;
82 uint32_t val = 0;
83 unsigned int pres = 0;
84
85 val = io_read32(master->base + master->layout->offset);
86
87 pres = (val >> master->layout->pres_shift) & MASTER_PRES_MASK;
88 if (pres != 3 || !charac->have_div3_pres)
89 pres = BIT(pres);
90
91 return UDIV_ROUND_NEAREST(parent_rate, pres);
92 }
93
clk_master_pres_get_parent(struct clk * clk)94 static size_t clk_master_pres_get_parent(struct clk *clk)
95 {
96 struct clk_master *master = clk->priv;
97 uint32_t mckr = 0;
98
99 mckr = io_read32(master->base + master->layout->offset);
100
101 return mckr & AT91_PMC_CSS;
102 }
103
104 static const struct clk_ops master_pres_ops = {
105 .enable = clk_master_enable,
106 .get_rate = clk_master_pres_get_rate,
107 .get_parent = clk_master_pres_get_parent,
108 };
109
110 static struct clk *
at91_clk_register_master_internal(struct pmc_data * pmc,const char * name,int num_parents,struct clk ** parents,const struct clk_master_layout * layout,const struct clk_master_charac * charac,const struct clk_ops * ops,int chg_pid)111 at91_clk_register_master_internal(struct pmc_data *pmc,
112 const char *name, int num_parents,
113 struct clk **parents,
114 const struct clk_master_layout *layout,
115 const struct clk_master_charac *charac,
116 const struct clk_ops *ops, int chg_pid)
117 {
118 struct clk_master *master = NULL;
119 struct clk *clk = NULL;
120
121 if (!name || !num_parents || !parents)
122 return NULL;
123
124 clk = clk_alloc(name, ops, parents, num_parents);
125 if (!clk)
126 return NULL;
127
128 master = calloc(1, sizeof(*master));
129 if (!master) {
130 clk_free(clk);
131 return NULL;
132 }
133
134 master->layout = layout;
135 master->charac = charac;
136 master->base = pmc->base;
137 master->chg_pid = chg_pid;
138
139 clk->priv = master;
140 clk->flags = CLK_SET_RATE_GATE;
141
142 if (clk_register(clk)) {
143 clk_free(clk);
144 free(master);
145 return NULL;
146 }
147
148 return clk;
149 }
150
151 struct clk *
at91_clk_register_master_pres(struct pmc_data * pmc,const char * name,int num_parents,struct clk ** parents,const struct clk_master_layout * layout,const struct clk_master_charac * charac,int chg_pid)152 at91_clk_register_master_pres(struct pmc_data *pmc,
153 const char *name, int num_parents,
154 struct clk **parents,
155 const struct clk_master_layout *layout,
156 const struct clk_master_charac *charac,
157 int chg_pid)
158 {
159 return at91_clk_register_master_internal(pmc, name, num_parents,
160 parents, layout,
161 charac,
162 &master_pres_ops, chg_pid);
163 }
164
165 struct clk *
at91_clk_register_master_div(struct pmc_data * pmc,const char * name,struct clk * parent,const struct clk_master_layout * layout,const struct clk_master_charac * charac)166 at91_clk_register_master_div(struct pmc_data *pmc,
167 const char *name, struct clk *parent,
168 const struct clk_master_layout *layout,
169 const struct clk_master_charac *charac)
170 {
171 return at91_clk_register_master_internal(pmc, name, 1,
172 &parent, layout,
173 charac,
174 &master_div_ops, -1);
175 }
176
177 const struct clk_master_layout at91sam9x5_master_layout = {
178 .mask = 0x373,
179 .pres_shift = 4,
180 .offset = AT91_PMC_MCKR,
181 };
182