[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/arm/at91/at91_pmc.c

Version: -  FREEBSD  -  FREEBSD7  -  FREEBSD71  -  FREEBSD70  -  FREEBSD6  -  FREEBSD64  -  FREEBSD63  -  FREEBSD62  -  FREEBSD61  -  FREEBSD60  -  FREEBSD5  -  FREEBSD55  -  FREEBSD54  -  FREEBSD53  -  FREEBSD52  -  FREEBSD51  -  FREEBSD50  -  FREEBSD4  -  FREEBSD3  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  OPENSOLARIS  -  minix-3-1-1  -  TRUSTEDBSD-SEBSD  -  FREEBSD-LIBC  -  FREEBSD7-LIBC  -  FREEBSD6-LIBC  -  GLIBC27 
SearchContext: -  none  -  excerpts  -  bigexcerpts 

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

Cache object: d04c9d7e89116ebf86d7c6e29f475321


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.