The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ 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  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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: releng/6.2/sys/arm/at91/at91_pmc.c 160474 2006-07-18 20:23:18Z imp $");
   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);

Cache object: c38b27d4317b41821b9c66494e34d2ed


[ 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.