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$");
   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         if (on) {
  166                 on = PMC_IER_LOCKB;
  167                 value = sc->pllb_init;
  168         } else {
  169                 value = 0;
  170         }
  171         WR4(sc, CKGR_PLLBR, value);
  172         while ((RD4(sc, PMC_SR) & PMC_IER_LOCKB) != on)
  173                 continue;
  174 }
  175 
  176 static void
  177 at91_pmc_set_sys_mode(struct at91_pmc_clock *clk, int on)
  178 {
  179         struct at91_pmc_softc *sc = pmc_softc;
  180 
  181         WR4(sc, on ? PMC_SCER : PMC_SCDR, clk->pmc_mask);
  182         if (on)
  183                 while ((RD4(sc, PMC_SCSR) & clk->pmc_mask) != clk->pmc_mask)
  184                         continue;
  185         else
  186                 while ((RD4(sc, PMC_SCSR) & clk->pmc_mask) == clk->pmc_mask)
  187                         continue;
  188 }
  189 
  190 static void
  191 at91_pmc_set_periph_mode(struct at91_pmc_clock *clk, int on)
  192 {
  193         struct at91_pmc_softc *sc = pmc_softc;
  194 
  195         WR4(sc, on ? PMC_PCER : PMC_PCDR, clk->pmc_mask);
  196         if (on)
  197                 while ((RD4(sc, PMC_PCSR) & clk->pmc_mask) != clk->pmc_mask)
  198                         continue;
  199         else
  200                 while ((RD4(sc, PMC_PCSR) & clk->pmc_mask) == clk->pmc_mask)
  201                         continue;
  202 }
  203 
  204 struct at91_pmc_clock *
  205 at91_pmc_clock_ref(const char *name)
  206 {
  207         int i;
  208 
  209         for (i = 0; i < sizeof(clock_list) / sizeof(clock_list[0]); i++)
  210                 if (strcmp(name, clock_list[i]->name) == 0)
  211                         return (clock_list[i]);
  212 
  213         return (NULL);
  214 }
  215 
  216 void
  217 at91_pmc_clock_deref(struct at91_pmc_clock *clk)
  218 {
  219 }
  220 
  221 void
  222 at91_pmc_clock_enable(struct at91_pmc_clock *clk)
  223 {
  224         /* XXX LOCKING? XXX */
  225         if (clk->parent)
  226                 at91_pmc_clock_enable(clk->parent);
  227         if (clk->refcnt++ == 0 && clk->set_mode)
  228                 clk->set_mode(clk, 1);
  229 }
  230 
  231 void
  232 at91_pmc_clock_disable(struct at91_pmc_clock *clk)
  233 {
  234         /* XXX LOCKING? XXX */
  235         if (--clk->refcnt == 0 && clk->set_mode)
  236                 clk->set_mode(clk, 0);
  237         if (clk->parent)
  238                 at91_pmc_clock_disable(clk->parent);
  239 }
  240 
  241 static int
  242 at91_pmc_pll_rate(int freq, uint32_t reg, int is_pllb)
  243 {
  244         uint32_t mul, div;
  245 
  246         div = reg & 0xff;
  247         mul = (reg >> 16) & 0x7ff;
  248         if (div != 0 && mul != 0) {
  249                 freq /= div;
  250                 freq *= mul + 1;
  251         } else {
  252                 freq = 0;
  253         }
  254         if (is_pllb && (reg & (1 << 28)))
  255                 freq >>= 1;
  256         return (freq);
  257 }
  258 
  259 static uint32_t
  260 at91_pmc_pll_calc(uint32_t main_freq, uint32_t out_freq)
  261 {
  262         uint32_t i, div = 0, mul = 0, diff = 1 << 30;
  263         unsigned ret = (out_freq > PMC_PLL_FAST_THRESH) ? 0xbe00 : 0x3e00; 
  264 
  265         if (out_freq > PMC_PLL_MAX_OUT_FREQ)
  266                 goto fail;
  267 
  268         for (i = 1; i < 256; i++) {
  269                 int32_t diff1;
  270                 uint32_t input, mul1;
  271 
  272                 input = main_freq / i;
  273                 if (input < PMC_PLL_MIN_IN_FREQ)
  274                         break;
  275                 if (input > PMC_PLL_MAX_IN_FREQ)
  276                         continue;
  277 
  278                 mul1 = out_freq / input;
  279                 if (mul1 > PMC_PLL_MULT_MAX)
  280                         continue;
  281                 if (mul1 < PMC_PLL_MULT_MIN)
  282                         break;
  283 
  284                 diff1 = out_freq - input * mul1;
  285                 if (diff1 < 0)
  286                         diff1 = -diff1;
  287                 if (diff > diff1) {
  288                         diff = diff1;
  289                         div = i;
  290                         mul = mul1;
  291                         if (diff == 0)
  292                                 break;
  293                 }
  294         }
  295         if (diff > (out_freq >> PMC_PLL_SHIFT_TOL))
  296                 goto fail;
  297         return ret | ((mul - 1) << 16) | div;
  298 fail:
  299         return 0;
  300 }
  301 
  302 static void
  303 at91_pmc_init_clock(struct at91_pmc_softc *sc, int main_clock)
  304 {
  305         uint32_t mckr;
  306         int freq;
  307 
  308         sc->main_clock_hz = main_clock;
  309         main_ck.hz = main_clock;
  310         plla.hz = at91_pmc_pll_rate(main_clock, RD4(sc, CKGR_PLLAR), 0);
  311 
  312         /*
  313          * Initialize the usb clock.  This sets up pllb, but disables the
  314          * actual clock.
  315          */
  316         sc->pllb_init = at91_pmc_pll_calc(main_clock, 48000000 * 2) |0x10000000;
  317         pllb.hz = at91_pmc_pll_rate(main_clock, sc->pllb_init, 1);
  318         WR4(sc, PMC_PCDR, (1 << AT91RM92_IRQ_UHP) | (1 << AT91RM92_IRQ_UDP));
  319         WR4(sc, PMC_SCDR, PMC_SCER_UHP | PMC_SCER_UDP);
  320         WR4(sc, CKGR_PLLBR, 0);
  321         WR4(sc, PMC_SCER, PMC_SCER_MCKUDP);
  322 
  323         /*
  324          * MCK and PCU derive from one of the primary clocks.  Initialize
  325          * this relationship.
  326          */
  327         mckr = RD4(sc, PMC_MCKR);
  328         mck.parent = clock_list[mckr & 0x3];
  329         mck.parent->refcnt++;
  330         freq = mck.parent->hz / (1 << ((mckr >> 2) & 3));
  331         mck.hz = freq / (1 + ((mckr >> 8) & 3));
  332 
  333         device_printf(sc->dev,
  334             "Primary: %d Hz PLLA: %d MHz CPU: %d MHz MCK: %d MHz\n",
  335             sc->main_clock_hz,
  336             at91_pmc_pll_rate(main_clock, RD4(sc, CKGR_PLLAR), 0) / 1000000,
  337             freq / 1000000, mck.hz / 1000000);
  338         WR4(sc, PMC_SCDR, PMC_SCER_PCK0 | PMC_SCER_PCK1 | PMC_SCER_PCK2 |
  339             PMC_SCER_PCK3);
  340         /* XXX kludge, turn on all peripherals */
  341         WR4(sc, PMC_PCER, 0xffffffff);
  342         /* Disable all interrupts for PMC */
  343         WR4(sc, PMC_IDR, 0xffffffff);
  344 }
  345 
  346 static void
  347 at91_pmc_deactivate(device_t dev)
  348 {
  349         struct at91_pmc_softc *sc;
  350 
  351         sc = device_get_softc(dev);
  352         bus_generic_detach(sc->dev);
  353         if (sc->mem_res)
  354                 bus_release_resource(dev, SYS_RES_IOPORT,
  355                     rman_get_rid(sc->mem_res), sc->mem_res);
  356         sc->mem_res = 0;
  357         return;
  358 }
  359 
  360 static int
  361 at91_pmc_activate(device_t dev)
  362 {
  363         struct at91_pmc_softc *sc;
  364         int rid;
  365 
  366         sc = device_get_softc(dev);
  367         rid = 0;
  368         sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
  369             RF_ACTIVE);
  370         if (sc->mem_res == NULL)
  371                 goto errout;
  372         return (0);
  373 errout:
  374         at91_pmc_deactivate(dev);
  375         return (ENOMEM);
  376 }
  377 
  378 static int
  379 at91_pmc_probe(device_t dev)
  380 {
  381 
  382         device_set_desc(dev, "PMC");
  383         return (0);
  384 }
  385 
  386 static int
  387 at91_pmc_attach(device_t dev)
  388 {
  389         int err;
  390 
  391         pmc_softc = device_get_softc(dev);
  392         pmc_softc->dev = dev;
  393         if ((err = at91_pmc_activate(dev)) != 0)
  394                 return err;
  395 #if defined(AT91_TSC) | defined (AT91_BWCT)
  396         at91_pmc_init_clock(pmc_softc, 16000000);
  397 #else
  398         at91_pmc_init_clock(pmc_softc, 10000000);
  399 #endif
  400 
  401         return (0);
  402 }
  403 
  404 static device_method_t at91_pmc_methods[] = {
  405         DEVMETHOD(device_probe, at91_pmc_probe),
  406         DEVMETHOD(device_attach, at91_pmc_attach),
  407         {0, 0},
  408 };
  409 
  410 static driver_t at91_pmc_driver = {
  411         "at91_pmc",
  412         at91_pmc_methods,
  413         sizeof(struct at91_pmc_softc),
  414 };
  415 static devclass_t at91_pmc_devclass;
  416 
  417 DRIVER_MODULE(at91_pmc, atmelarm, at91_pmc_driver, at91_pmc_devclass, 0, 0);

Cache object: 6bcaf051803883cd7506759bd37c99f2


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