]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - 6/sys/arm/at91/at91_pmc.c
merge fix for boot-time hang on centos' xen
[FreeBSD/FreeBSD.git] / 6 / sys / arm / at91 / at91_pmc.c
1 /*-
2  * Copyright (c) 2006 M. Warner Losh.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include "opt_at91.h"
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/module.h>
34 #include <sys/time.h>
35 #include <sys/bus.h>
36 #include <sys/resource.h>
37 #include <sys/rman.h>
38 #include <sys/timetc.h>
39
40 #include <machine/bus.h>
41 #include <machine/cpu.h>
42 #include <machine/cpufunc.h>
43 #include <machine/resource.h>
44 #include <machine/frame.h>
45 #include <machine/intr.h>
46 #include <arm/at91/at91rm92reg.h>
47
48 #include <arm/at91/at91_pmcreg.h>
49 #include <arm/at91/at91_pmcvar.h>
50
51 static struct at91_pmc_softc {
52         bus_space_tag_t         sc_st;
53         bus_space_handle_t      sc_sh;
54         struct resource *mem_res;       /* Memory resource */
55         device_t                dev;
56         int                     main_clock_hz;
57         uint32_t                pllb_init;
58 } *pmc_softc;
59
60 static void at91_pmc_set_pllb_mode(struct at91_pmc_clock *, int);
61 static void at91_pmc_set_sys_mode(struct at91_pmc_clock *, int);
62 static void at91_pmc_set_periph_mode(struct at91_pmc_clock *, int);
63
64 static struct at91_pmc_clock slck = {
65         .name = "slck",         // 32,768 Hz slow clock
66         .hz = 32768,
67         .refcnt = 1,
68         .id = 0,
69         .primary = 1,
70 };
71
72 static struct at91_pmc_clock main_ck = {
73         .name = "main",         // Main clock
74         .refcnt = 0,
75         .id = 1,
76         .primary = 1,
77         .pmc_mask = PMC_IER_MOSCS,
78 };
79
80 static struct at91_pmc_clock plla = {
81         .name = "plla",         // PLLA Clock, used for CPU clocking
82         .parent = &main_ck,
83         .refcnt = 1,
84         .id = 0,
85         .primary = 1,
86         .pll = 1,
87         .pmc_mask = PMC_IER_LOCKA,
88 };
89
90 static struct at91_pmc_clock pllb = {
91         .name = "pllb",         // PLLB Clock, used for USB functions
92         .parent = &main_ck,
93         .refcnt = 0,
94         .id = 0,
95         .primary = 1,
96         .pll = 1,
97         .pmc_mask = PMC_IER_LOCKB,
98         .set_mode = &at91_pmc_set_pllb_mode,
99 };
100
101 static struct at91_pmc_clock udpck = {
102         .name = "udpck",
103         .parent = &pllb,
104         .pmc_mask = PMC_SCER_UDP,
105         .set_mode = at91_pmc_set_sys_mode
106 };
107
108 static struct at91_pmc_clock uhpck = {
109         .name = "uhpck",
110         .parent = &pllb,
111         .pmc_mask = PMC_SCER_UHP,
112         .set_mode = at91_pmc_set_sys_mode
113 };
114
115 static struct at91_pmc_clock mck = {
116         .name = "mck",
117         .pmc_mask = PMC_IER_MCKRDY,
118         .refcnt = 0,
119 };
120
121 static struct at91_pmc_clock udc_clk = {
122         .name = "udc_clk",
123         .parent = &mck,
124         .pmc_mask = 1 << AT91RM92_IRQ_UDP,
125         .set_mode = &at91_pmc_set_periph_mode
126 };
127
128 static struct at91_pmc_clock ohci_clk = {
129         .name = "ohci_clk",
130         .parent = &mck,
131         .pmc_mask = 1 << AT91RM92_IRQ_UDP,
132         .set_mode = &at91_pmc_set_periph_mode
133 };
134
135 static struct at91_pmc_clock *const clock_list[] = {
136         &slck,
137         &main_ck,
138         &plla,
139         &pllb,
140         &udpck,
141         &uhpck,
142         &mck,
143         &udc_clk,
144         &ohci_clk
145 };
146
147 static inline uint32_t
148 RD4(struct at91_pmc_softc *sc, bus_size_t off)
149 {
150         return bus_read_4(sc->mem_res, off);
151 }
152
153 static inline void
154 WR4(struct at91_pmc_softc *sc, bus_size_t off, uint32_t val)
155 {
156         bus_write_4(sc->mem_res, off, val);
157 }
158
159 static void
160 at91_pmc_set_pllb_mode(struct at91_pmc_clock *clk, int on)
161 {
162         struct at91_pmc_softc *sc = pmc_softc;
163         uint32_t value;
164
165         printf("Turning PLLB %#x %s\n", sc->pllb_init, on ? "on" : "off");
166         if (on) {
167                 on = PMC_IER_LOCKB;
168                 value = sc->pllb_init;
169         } else {
170                 value = 0;
171         }
172         WR4(sc, CKGR_PLLBR, value);
173         while ((RD4(sc, PMC_SR) & PMC_IER_LOCKB) != on)
174                 continue;
175         printf("Done!\n");
176 }
177
178 static void
179 at91_pmc_set_sys_mode(struct at91_pmc_clock *clk, int on)
180 {
181         struct at91_pmc_softc *sc = pmc_softc;
182
183         printf("Turning SC %#x %s\n", clk->pmc_mask, on ? "on" : "off");
184         WR4(sc, on ? PMC_SCER : PMC_SCDR, clk->pmc_mask);
185         if (on)
186                 while ((RD4(sc, PMC_SCSR) & clk->pmc_mask) != clk->pmc_mask)
187                         continue;
188         else
189                 while ((RD4(sc, PMC_SCSR) & clk->pmc_mask) == clk->pmc_mask)
190                         continue;
191         printf("Done SCSR is now: %#x!\n", RD4(sc, PMC_SCSR));
192 }
193
194 static void
195 at91_pmc_set_periph_mode(struct at91_pmc_clock *clk, int on)
196 {
197         struct at91_pmc_softc *sc = pmc_softc;
198
199         printf("Turning PC %#x %s\n", clk->pmc_mask, on ? "on" : "off");
200         WR4(sc, on ? PMC_PCER : PMC_PCDR, clk->pmc_mask);
201         if (on)
202                 while ((RD4(sc, PMC_PCSR) & clk->pmc_mask) != clk->pmc_mask)
203                         continue;
204         else
205                 while ((RD4(sc, PMC_PCSR) & clk->pmc_mask) == clk->pmc_mask)
206                         continue;
207         printf("Done PCSR is now: %#x!\n", RD4(sc, PMC_PCSR));
208 }
209
210 struct at91_pmc_clock *
211 at91_pmc_clock_ref(const char *name)
212 {
213         int i;
214
215         for (i = 0; i < sizeof(clock_list) / sizeof(clock_list[0]); i++)
216                 if (strcmp(name, clock_list[i]->name) == 0)
217                         return (clock_list[i]);
218
219         return (NULL);
220 }
221
222 void
223 at91_pmc_clock_deref(struct at91_pmc_clock *clk)
224 {
225 }
226
227 void
228 at91_pmc_clock_enable(struct at91_pmc_clock *clk)
229 {
230         /* XXX LOCKING? XXX */
231         printf("Enable %s\n", clk->name);
232         if (clk->parent)
233                 at91_pmc_clock_enable(clk->parent);
234         if (clk->refcnt++ == 0 && clk->set_mode)
235                 clk->set_mode(clk, 1);
236 }
237
238 void
239 at91_pmc_clock_disable(struct at91_pmc_clock *clk)
240 {
241         /* XXX LOCKING? XXX */
242         if (--clk->refcnt == 0 && clk->set_mode)
243                 clk->set_mode(clk, 0);
244         if (clk->parent)
245                 at91_pmc_clock_disable(clk->parent);
246 }
247
248 static int
249 at91_pmc_pll_rate(int freq, uint32_t reg, int is_pllb)
250 {
251         uint32_t mul, div;
252
253         div = reg & 0xff;
254         mul = (reg >> 16) & 0x7ff;
255         if (div != 0 && mul != 0) {
256                 freq /= div;
257                 freq *= mul + 1;
258         } else {
259                 freq = 0;
260         }
261         if (is_pllb && (reg & (1 << 28)))
262                 freq >>= 1;
263         return (freq);
264 }
265
266 static uint32_t
267 at91_pmc_pll_calc(uint32_t main_freq, uint32_t out_freq)
268 {
269         uint32_t i, div = 0, mul = 0, diff = 1 << 30;
270         unsigned ret = (out_freq > PMC_PLL_FAST_THRESH) ? 0xbe00 : 0x3e00; 
271
272         if (out_freq > PMC_PLL_MAX_OUT_FREQ)
273                 goto fail;
274
275         for (i = 1; i < 256; i++) {
276                 int32_t diff1;
277                 uint32_t input, mul1;
278
279                 input = main_freq / i;
280                 if (input < PMC_PLL_MIN_IN_FREQ)
281                         break;
282                 if (input > PMC_PLL_MAX_IN_FREQ)
283                         continue;
284
285                 mul1 = out_freq / input;
286                 if (mul1 > PMC_PLL_MULT_MAX)
287                         continue;
288                 if (mul1 < PMC_PLL_MULT_MIN)
289                         break;
290
291                 diff1 = out_freq - input * mul1;
292                 if (diff1 < 0)
293                         diff1 = -diff1;
294                 if (diff > diff1) {
295                         diff = diff1;
296                         div = i;
297                         mul = mul1;
298                         if (diff == 0)
299                                 break;
300                 }
301         }
302         if (diff > (out_freq >> PMC_PLL_SHIFT_TOL))
303                 goto fail;
304         return ret | ((mul - 1) << 16) | div;
305 fail:
306         return 0;
307 }
308
309 static void
310 at91_pmc_init_clock(struct at91_pmc_softc *sc, int main_clock)
311 {
312         uint32_t mckr;
313         int freq;
314
315         sc->main_clock_hz = main_clock;
316         main_ck.hz = main_clock;
317         plla.hz = at91_pmc_pll_rate(main_clock, RD4(sc, CKGR_PLLAR), 0);
318
319         /*
320          * Initialize the usb clock.  This sets up pllb, but disables the
321          * actual clock.
322          */
323         sc->pllb_init = at91_pmc_pll_calc(main_clock, 48000000 * 2) |0x10000000;
324         pllb.hz = at91_pmc_pll_rate(main_clock, sc->pllb_init, 1);
325         WR4(sc, PMC_PCDR, (1 << AT91RM92_IRQ_UHP) | (1 << AT91RM92_IRQ_UDP));
326         WR4(sc, PMC_SCDR, PMC_SCER_UHP | PMC_SCER_UDP);
327         WR4(sc, CKGR_PLLBR, 0);
328         WR4(sc, PMC_SCER, PMC_SCER_MCKUDP);
329
330         /*
331          * MCK and PCU derive from one of the primary clocks.  Initialize
332          * this relationship.
333          */
334         mckr = RD4(sc, PMC_MCKR);
335         mck.parent = clock_list[mckr & 0x3];
336         mck.parent->refcnt++;
337         freq = mck.parent->hz / (1 << ((mckr >> 2) & 3));
338         mck.hz = freq / (1 + ((mckr >> 8) & 3));
339
340         device_printf(sc->dev,
341             "Primary: %d Hz PLLA: %d MHz CPU: %d MHz MCK: %d MHz\n",
342             sc->main_clock_hz,
343             at91_pmc_pll_rate(main_clock, RD4(sc, CKGR_PLLAR), 0) / 1000000,
344             freq / 1000000, mck.hz / 1000000);
345         WR4(sc, PMC_SCDR, PMC_SCER_PCK0 | PMC_SCER_PCK1 | PMC_SCER_PCK2 |
346             PMC_SCER_PCK3);
347         /* XXX kludge, turn on all peripherals */
348         WR4(sc, PMC_PCER, 0xffffffff);
349         /* Disable all interrupts for PMC */
350         WR4(sc, PMC_IDR, 0xffffffff);
351 }
352
353 static void
354 at91_pmc_deactivate(device_t dev)
355 {
356         struct at91_pmc_softc *sc;
357
358         sc = device_get_softc(dev);
359         bus_generic_detach(sc->dev);
360         if (sc->mem_res)
361                 bus_release_resource(dev, SYS_RES_IOPORT,
362                     rman_get_rid(sc->mem_res), sc->mem_res);
363         sc->mem_res = 0;
364         return;
365 }
366
367 static int
368 at91_pmc_activate(device_t dev)
369 {
370         struct at91_pmc_softc *sc;
371         int rid;
372
373         sc = device_get_softc(dev);
374         rid = 0;
375         sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
376             RF_ACTIVE);
377         if (sc->mem_res == NULL)
378                 goto errout;
379         return (0);
380 errout:
381         at91_pmc_deactivate(dev);
382         return (ENOMEM);
383 }
384
385 static int
386 at91_pmc_probe(device_t dev)
387 {
388
389         device_set_desc(dev, "PMC");
390         return (0);
391 }
392
393 static int
394 at91_pmc_attach(device_t dev)
395 {
396         int err;
397
398         pmc_softc = device_get_softc(dev);
399         pmc_softc->dev = dev;
400         if ((err = at91_pmc_activate(dev)) != 0)
401                 return err;
402 #ifdef AT91_TSC
403         at91_pmc_init_clock(pmc_softc, 16000000);
404 #else
405         at91_pmc_init_clock(pmc_softc, 10000000);
406 #endif
407
408         return (0);
409 }
410
411 static device_method_t at91_pmc_methods[] = {
412         DEVMETHOD(device_probe, at91_pmc_probe),
413         DEVMETHOD(device_attach, at91_pmc_attach),
414         {0, 0},
415 };
416
417 static driver_t at91_pmc_driver = {
418         "at91_pmc",
419         at91_pmc_methods,
420         sizeof(struct at91_pmc_softc),
421 };
422 static devclass_t at91_pmc_devclass;
423
424 DRIVER_MODULE(at91_pmc, atmelarm, at91_pmc_driver, at91_pmc_devclass, 0, 0);