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  * Copyright (c) 2010 Greg Ansley.  All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  */
   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/malloc.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/at91reg.h>
   48 #include <arm/at91/at91var.h>
   49 
   50 #include <arm/at91/at91_pmcreg.h>
   51 #include <arm/at91/at91_pmcvar.h>
   52 
   53 static struct at91_pmc_softc {
   54         bus_space_tag_t         sc_st;
   55         bus_space_handle_t      sc_sh;
   56         struct resource *mem_res;       /* Memory resource */
   57         device_t                dev;
   58         unsigned int            main_clock_hz;
   59         uint32_t                pllb_init;
   60 } *pmc_softc;
   61 
   62 MALLOC_DECLARE(M_PMC);
   63 MALLOC_DEFINE(M_PMC, "at91_pmc_clocks", "AT91 PMC Clock descriptors");
   64 
   65 static void at91_pmc_set_pllb_mode(struct at91_pmc_clock *, int);
   66 static void at91_pmc_set_sys_mode(struct at91_pmc_clock *, int);
   67 static void at91_pmc_set_periph_mode(struct at91_pmc_clock *, int);
   68 static void at91_pmc_clock_alias(const char *name, const char *alias);
   69 
   70 static struct at91_pmc_clock slck = {
   71         .name = "slck",         // 32,768 Hz slow clock
   72         .hz = 32768,
   73         .refcnt = 1,
   74         .id = 0,
   75         .primary = 1,
   76 };
   77 
   78 /*
   79  * NOTE: Clocks for "ordinary peripheral" devices e.g. spi0, udp0, uhp0 etc.
   80  * are now created automatically. Only "system" clocks need be defined here.
   81  */
   82 static struct at91_pmc_clock main_ck = {
   83         .name = "main",         // Main clock
   84         .refcnt = 0,
   85         .id = 1,
   86         .primary = 1,
   87         .pmc_mask = PMC_IER_MOSCS,
   88 };
   89 
   90 static struct at91_pmc_clock plla = {
   91         .name = "plla",         // PLLA Clock, used for CPU clocking
   92         .parent = &main_ck,
   93         .refcnt = 1,
   94         .id = 0,
   95         .primary = 1,
   96         .pll = 1,
   97         .pmc_mask = PMC_IER_LOCKA,
   98 };
   99 
  100 static struct at91_pmc_clock pllb = {
  101         .name = "pllb",         // PLLB Clock, used for USB functions
  102         .parent = &main_ck,
  103         .refcnt = 0,
  104         .id = 0,
  105         .primary = 1,
  106         .pll = 1,
  107         .pmc_mask = PMC_IER_LOCKB,
  108         .set_mode = &at91_pmc_set_pllb_mode,
  109 };
  110 
  111 static struct at91_pmc_clock udpck = {
  112         .name = "udpck",
  113         .parent = &pllb,
  114         .pmc_mask = PMC_SCER_UDP,
  115         .set_mode = at91_pmc_set_sys_mode
  116 };
  117 
  118 static struct at91_pmc_clock uhpck = {
  119         .name = "uhpck",
  120         .parent = &pllb,
  121         .pmc_mask = PMC_SCER_UHP,
  122         .set_mode = at91_pmc_set_sys_mode
  123 };
  124 
  125 static struct at91_pmc_clock mck = {
  126         .name = "mck",          // Master (Peripheral) Clock
  127         .pmc_mask = PMC_IER_MCKRDY,
  128         .refcnt = 0,
  129 };
  130 
  131 static struct at91_pmc_clock cpu = {
  132         .name = "cpu",          // CPU Clock
  133         .parent = &plla,
  134         .pmc_mask = PMC_SCER_PCK,
  135         .refcnt = 0,
  136 };
  137 
  138 /* "+32" or the automatic peripheral clocks */
  139 static struct at91_pmc_clock *clock_list[16+32] = {
  140         &slck,
  141         &main_ck,
  142         &plla,
  143         &pllb,
  144         &udpck,
  145         &uhpck,
  146         &mck,
  147         &cpu
  148 };
  149 
  150 #if !defined(AT91C_MAIN_CLOCK)
  151 static const unsigned int at91_mainf_tbl[] = {
  152         3000000, 3276800, 3686400, 3840000, 4000000,
  153         4433619, 4915200, 5000000, 5242880, 6000000,
  154         6144000, 6400000, 6553600, 7159090, 7372800,
  155         7864320, 8000000, 9830400, 10000000, 11059200,
  156         12000000, 12288000, 13560000, 14318180, 14745600,
  157         16000000, 17344700, 18432000, 20000000
  158 };
  159 #define MAINF_TBL_LEN   (sizeof(at91_mainf_tbl) / sizeof(*at91_mainf_tbl))
  160 #endif
  161 
  162 static inline uint32_t
  163 RD4(struct at91_pmc_softc *sc, bus_size_t off)
  164 {
  165 
  166         return (bus_read_4(sc->mem_res, off));
  167 }
  168 
  169 static inline void
  170 WR4(struct at91_pmc_softc *sc, bus_size_t off, uint32_t val)
  171 {
  172 
  173         bus_write_4(sc->mem_res, off, val);
  174 }
  175 
  176 void
  177 at91_pmc_set_pllb_mode(struct at91_pmc_clock *clk, int on)
  178 {
  179         struct at91_pmc_softc *sc = pmc_softc;
  180         uint32_t value;
  181 
  182         if (on) {
  183                 on = PMC_IER_LOCKB;
  184                 value = sc->pllb_init;
  185         } else
  186                 value = 0;
  187 
  188         /* Workaround RM9200 Errata #26 */
  189         if (at91_is_rm92() &&
  190            ((value ^ RD4(sc, CKGR_PLLBR)) & 0x03f0ff) != 0) {
  191                 WR4(sc, CKGR_PLLBR, value ^ 1);
  192                 while ((RD4(sc, PMC_SR) & PMC_IER_LOCKB) != on)
  193                         continue;
  194         }
  195 
  196         WR4(sc, CKGR_PLLBR, value);
  197         while ((RD4(sc, PMC_SR) & PMC_IER_LOCKB) != on)
  198                 continue;
  199 }
  200 
  201 static void
  202 at91_pmc_set_sys_mode(struct at91_pmc_clock *clk, int on)
  203 {
  204         struct at91_pmc_softc *sc = pmc_softc;
  205 
  206         WR4(sc, on ? PMC_SCER : PMC_SCDR, clk->pmc_mask);
  207         if (on)
  208                 while ((RD4(sc, PMC_SCSR) & clk->pmc_mask) != clk->pmc_mask)
  209                         continue;
  210         else
  211                 while ((RD4(sc, PMC_SCSR) & clk->pmc_mask) == clk->pmc_mask)
  212                         continue;
  213 }
  214 
  215 static void
  216 at91_pmc_set_periph_mode(struct at91_pmc_clock *clk, int on)
  217 {
  218         struct at91_pmc_softc *sc = pmc_softc;
  219 
  220         WR4(sc, on ? PMC_PCER : PMC_PCDR, clk->pmc_mask);
  221         if (on)
  222                 while ((RD4(sc, PMC_PCSR) & clk->pmc_mask) != clk->pmc_mask)
  223                         continue;
  224         else
  225                 while ((RD4(sc, PMC_PCSR) & clk->pmc_mask) == clk->pmc_mask)
  226                         continue;
  227 }
  228 
  229 struct at91_pmc_clock *
  230 at91_pmc_clock_add(const char *name, uint32_t irq,
  231     struct at91_pmc_clock *parent)
  232 {
  233         struct at91_pmc_clock *clk;
  234         int i, buflen;
  235 
  236         clk = malloc(sizeof(*clk), M_PMC, M_NOWAIT | M_ZERO);
  237         if (clk == NULL)
  238                 goto err;
  239 
  240         buflen = strlen(name) + 1;
  241         clk->name = malloc(buflen, M_PMC, M_NOWAIT);
  242         if (clk->name == NULL)
  243                 goto err;
  244 
  245         strlcpy(clk->name, name, buflen);
  246         clk->pmc_mask = 1 << irq;
  247         clk->set_mode = &at91_pmc_set_periph_mode;
  248         if (parent == NULL)
  249                 clk->parent = &mck;
  250         else
  251                 clk->parent = parent;
  252 
  253         for (i = 0; i < sizeof(clock_list) / sizeof(clock_list[0]); i++) {
  254                 if (clock_list[i] == NULL) {
  255                         clock_list[i] = clk;
  256                         return (clk);
  257                 }
  258         }
  259 err:
  260         if (clk != NULL) {
  261                 if (clk->name != NULL)
  262                         free(clk->name, M_PMC);
  263                 free(clk, M_PMC);
  264         }
  265 
  266         panic("could not allocate pmc clock '%s'", name);
  267         return (NULL);
  268 }
  269 
  270 static void
  271 at91_pmc_clock_alias(const char *name, const char *alias)
  272 {
  273         struct at91_pmc_clock *clk, *alias_clk;
  274 
  275         clk = at91_pmc_clock_ref(name);
  276         if (clk)
  277                 alias_clk = at91_pmc_clock_add(alias, 0, clk->parent);
  278 
  279         if (clk && alias_clk) {
  280                 alias_clk->hz = clk->hz;
  281                 alias_clk->pmc_mask = clk->pmc_mask;
  282                 alias_clk->set_mode = clk->set_mode;
  283         }
  284 }
  285 
  286 struct at91_pmc_clock *
  287 at91_pmc_clock_ref(const char *name)
  288 {
  289         int i;
  290 
  291         for (i = 0; i < sizeof(clock_list) / sizeof(clock_list[0]); i++) {
  292                 if (clock_list[i] == NULL)
  293                     break;
  294                 if (strcmp(name, clock_list[i]->name) == 0)
  295                         return (clock_list[i]);
  296         }
  297 
  298         //printf("at91_pmc: Warning - did not find clock '%s'", name);
  299         return (NULL);
  300 }
  301 
  302 void
  303 at91_pmc_clock_deref(struct at91_pmc_clock *clk)
  304 {
  305 
  306 }
  307 
  308 void
  309 at91_pmc_clock_enable(struct at91_pmc_clock *clk)
  310 {
  311 
  312         /* XXX LOCKING? XXX */
  313         if (clk->parent)
  314                 at91_pmc_clock_enable(clk->parent);
  315         if (clk->refcnt++ == 0 && clk->set_mode)
  316                 clk->set_mode(clk, 1);
  317 }
  318 
  319 void
  320 at91_pmc_clock_disable(struct at91_pmc_clock *clk)
  321 {
  322 
  323         /* XXX LOCKING? XXX */
  324         if (--clk->refcnt == 0 && clk->set_mode)
  325                 clk->set_mode(clk, 0);
  326         if (clk->parent)
  327                 at91_pmc_clock_disable(clk->parent);
  328 }
  329 
  330 static int
  331 at91_pmc_pll_rate(struct at91_pmc_clock *clk, uint32_t reg)
  332 {
  333         uint32_t mul, div, freq;
  334 
  335         freq = clk->parent->hz;
  336         div = (reg >> clk->pll_div_shift) & clk->pll_div_mask;
  337         mul = (reg >> clk->pll_mul_shift) & clk->pll_mul_mask;
  338 
  339 #if 0
  340         printf("pll = (%d /  %d) * %d = %d\n",
  341             freq, div, mul + 1, (freq/div) * (mul+1));
  342 #endif
  343 
  344         if (div != 0 && mul != 0) {
  345                 freq /= div;
  346                 freq *= mul + 1;
  347         } else
  348                 freq = 0;
  349         clk->hz = freq;
  350 
  351         return (freq);
  352 }
  353 
  354 static uint32_t
  355 at91_pmc_pll_calc(struct at91_pmc_clock *clk, uint32_t out_freq)
  356 {
  357         uint32_t i, div = 0, mul = 0, diff = 1 << 30;
  358 
  359         unsigned ret = 0x3e00;
  360 
  361         if (out_freq > clk->pll_max_out)
  362                 goto fail;
  363 
  364         for (i = 1; i < 256; i++) {
  365                 int32_t diff1;
  366                 uint32_t input, mul1;
  367 
  368                 input = clk->parent->hz / i;
  369                 if (input < clk->pll_min_in)
  370                         break;
  371                 if (input > clk->pll_max_in)
  372                         continue;
  373 
  374                 mul1 = out_freq / input;
  375                 if (mul1 > (clk->pll_mul_mask + 1))
  376                         continue;
  377                 if (mul1 == 0)
  378                         break;
  379 
  380                 diff1 = out_freq - input * mul1;
  381                 if (diff1 < 0)
  382                         diff1 = -diff1;
  383                 if (diff > diff1) {
  384                         diff = diff1;
  385                         div = i;
  386                         mul = mul1;
  387                         if (diff == 0)
  388                                 break;
  389                 }
  390         }
  391         if (diff > (out_freq >> PMC_PLL_SHIFT_TOL))
  392                 goto fail;
  393 
  394         if (clk->set_outb != NULL)
  395                 ret |= clk->set_outb(out_freq);
  396 
  397         return (ret |
  398                 ((mul - 1) << clk->pll_mul_shift) |
  399                 (div << clk->pll_div_shift));
  400 fail:
  401         return (0);
  402 }
  403 
  404 static void
  405 at91_pmc_init_clock(struct at91_pmc_softc *sc, unsigned int main_clock)
  406 {
  407         uint32_t mckr;
  408         uint32_t mdiv;
  409 
  410         if (at91_is_sam9() || at91_is_sam9xe()) {
  411                 uhpck.pmc_mask = PMC_SCER_UHP_SAM9;
  412                 udpck.pmc_mask = PMC_SCER_UDP_SAM9;
  413         }
  414         mckr = RD4(sc, PMC_MCKR);
  415         sc->main_clock_hz = main_clock;
  416         main_ck.hz = main_clock;
  417 
  418         at91_pmc_pll_rate(&plla, RD4(sc, CKGR_PLLAR));
  419 
  420         if (at91_cpu_is(AT91_CPU_SAM9G45) && (mckr & PMC_MCKR_PLLADIV2))
  421                 plla.hz /= 2;
  422 
  423         /*
  424          * Initialize the usb clock.  This sets up pllb, but disables the
  425          * actual clock.
  426          */
  427         sc->pllb_init = at91_pmc_pll_calc(&pllb, 48000000 * 2) | 0x10000000;
  428         at91_pmc_pll_rate(&pllb, sc->pllb_init);
  429 
  430 #if 0
  431         /* Turn off USB clocks */
  432         at91_pmc_set_periph_mode(&ohci_clk, 0);
  433         at91_pmc_set_periph_mode(&udc_clk, 0);
  434 #endif
  435 
  436         if (at91_is_rm92()) {
  437                 WR4(sc, PMC_SCDR, PMC_SCER_UHP | PMC_SCER_UDP);
  438                 WR4(sc, PMC_SCER, PMC_SCER_MCKUDP);
  439         } else
  440                 WR4(sc, PMC_SCDR, PMC_SCER_UHP_SAM9 | PMC_SCER_UDP_SAM9);
  441         WR4(sc, CKGR_PLLBR, 0);
  442 
  443         /*
  444          * MCK and PCU derive from one of the primary clocks.  Initialize
  445          * this relationship.
  446          */
  447         mck.parent = clock_list[mckr & 0x3];
  448         mck.parent->refcnt++;
  449 
  450         cpu.hz = mck.hz = mck.parent->hz /
  451             (1 << ((mckr & PMC_MCKR_PRES_MASK) >> 2));
  452 
  453         mdiv = (mckr & PMC_MCKR_MDIV_MASK) >> 8;
  454         if (at91_is_sam9() || at91_is_sam9xe()) {
  455                 if (mdiv > 0)
  456                         mck.hz /= mdiv * 2;
  457         } else
  458                 mck.hz /= (1 + mdiv);
  459 
  460         /* Only found on SAM9G20 */
  461         if (at91_cpu_is(AT91_CPU_SAM9G20))
  462                 cpu.hz /= (mckr & PMC_MCKR_PDIV) ?  2 : 1;
  463 
  464         at91_master_clock = mck.hz;
  465 
  466         device_printf(sc->dev,
  467             "Primary: %d Hz PLLA: %d MHz CPU: %d MHz MCK: %d MHz\n",
  468             sc->main_clock_hz,
  469             plla.hz / 1000000,
  470             cpu.hz / 1000000, mck.hz / 1000000);
  471 
  472         /* Turn off "Progamable" clocks */
  473         WR4(sc, PMC_SCDR, PMC_SCER_PCK0 | PMC_SCER_PCK1 | PMC_SCER_PCK2 |
  474             PMC_SCER_PCK3);
  475 
  476         /* XXX kludge, turn on all peripherals */
  477         WR4(sc, PMC_PCER, 0xffffffff);
  478 
  479         /* Disable all interrupts for PMC */
  480         WR4(sc, PMC_IDR, 0xffffffff);
  481 }
  482 
  483 static void
  484 at91_pmc_deactivate(device_t dev)
  485 {
  486         struct at91_pmc_softc *sc;
  487 
  488         sc = device_get_softc(dev);
  489         bus_generic_detach(sc->dev);
  490         if (sc->mem_res)
  491                 bus_release_resource(dev, SYS_RES_IOPORT,
  492                     rman_get_rid(sc->mem_res), sc->mem_res);
  493         sc->mem_res = 0;
  494 }
  495 
  496 static int
  497 at91_pmc_activate(device_t dev)
  498 {
  499         struct at91_pmc_softc *sc;
  500         int rid;
  501 
  502         sc = device_get_softc(dev);
  503         rid = 0;
  504         sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
  505             RF_ACTIVE);
  506         if (sc->mem_res == NULL)
  507                 goto errout;
  508         return (0);
  509 errout:
  510         at91_pmc_deactivate(dev);
  511         return (ENOMEM);
  512 }
  513 
  514 static int
  515 at91_pmc_probe(device_t dev)
  516 {
  517 
  518         device_set_desc(dev, "PMC");
  519         return (0);
  520 }
  521 
  522 #if !defined(AT91C_MAIN_CLOCK)
  523 static unsigned int
  524 at91_pmc_sense_mainf(struct at91_pmc_softc *sc)
  525 {
  526         unsigned int ckgr_val;
  527         unsigned int diff, matchdiff;
  528         int i, match;
  529 
  530         ckgr_val = (RD4(sc, CKGR_MCFR) & CKGR_MCFR_MAINF_MASK) << 11;
  531 
  532         /*
  533          * Try to find the standard frequency that match best.
  534          */
  535         match = 0;
  536         matchdiff = abs(ckgr_val - at91_mainf_tbl[0]);
  537         for (i = 1; i < MAINF_TBL_LEN; i++) {
  538                 diff = abs(ckgr_val - at91_mainf_tbl[i]);
  539                 if (diff < matchdiff) {
  540                         match = i;
  541                         matchdiff = diff;
  542                 }
  543         }
  544         return (at91_mainf_tbl[match]);
  545 }
  546 #endif
  547 
  548 static int
  549 at91_pmc_attach(device_t dev)
  550 {
  551         unsigned int mainf;
  552         int err;
  553 
  554         pmc_softc = device_get_softc(dev);
  555         pmc_softc->dev = dev;
  556         if ((err = at91_pmc_activate(dev)) != 0)
  557                 return (err);
  558 
  559         /*
  560          * Configure main clock frequency.
  561          */
  562 #if !defined(AT91C_MAIN_CLOCK)
  563         mainf = at91_pmc_sense_mainf(pmc_softc);
  564 #else
  565         mainf = AT91C_MAIN_CLOCK;
  566 #endif
  567         at91_pmc_init_clock(pmc_softc, mainf);
  568 
  569         /* These clocks refrenced by "special" names */
  570         at91_pmc_clock_alias("ohci0", "ohci_clk");
  571         at91_pmc_clock_alias("udp0",  "udp_clk");
  572 
  573         return (0);
  574 }
  575 
  576 static device_method_t at91_pmc_methods[] = {
  577         DEVMETHOD(device_probe, at91_pmc_probe),
  578         DEVMETHOD(device_attach, at91_pmc_attach),
  579         DEVMETHOD_END
  580 };
  581 
  582 static driver_t at91_pmc_driver = {
  583         "at91_pmc",
  584         at91_pmc_methods,
  585         sizeof(struct at91_pmc_softc),
  586 };
  587 static devclass_t at91_pmc_devclass;
  588 
  589 DRIVER_MODULE(at91_pmc, atmelarm, at91_pmc_driver, at91_pmc_devclass, NULL,
  590     NULL);

Cache object: 083ceaa8bffdebaf11d65d1c1607ccdb


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