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: releng/9.0/sys/arm/at91/at91_pmc.c 216227 2010-12-06 10:24:06Z kevlo $");
   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         return (bus_read_4(sc->mem_res, off));
  166 }
  167 
  168 static inline void
  169 WR4(struct at91_pmc_softc *sc, bus_size_t off, uint32_t val)
  170 {
  171         bus_write_4(sc->mem_res, off, val);
  172 }
  173 
  174 void
  175 at91_pmc_set_pllb_mode(struct at91_pmc_clock *clk, int on)
  176 {
  177         struct at91_pmc_softc *sc = pmc_softc;
  178         uint32_t value;
  179 
  180         if (on) {
  181                 on = PMC_IER_LOCKB;
  182                 value = sc->pllb_init;
  183         } else {
  184                 value = 0;
  185         }
  186 
  187         /* Workaround RM9200 Errata #26 */
  188         if (at91_is_rm92() &&
  189            ((value ^ RD4(sc, CKGR_PLLBR)) & 0x03f0ff) != 0) {
  190                 WR4(sc, CKGR_PLLBR, value ^ 1);
  191                 while ((RD4(sc, PMC_SR) & PMC_IER_LOCKB) != on)
  192                         continue;
  193         }
  194 
  195         WR4(sc, CKGR_PLLBR, value);
  196         while ((RD4(sc, PMC_SR) & PMC_IER_LOCKB) != on)
  197                 continue;
  198 }
  199 
  200 static void
  201 at91_pmc_set_sys_mode(struct at91_pmc_clock *clk, int on)
  202 {
  203         struct at91_pmc_softc *sc = pmc_softc;
  204 
  205         WR4(sc, on ? PMC_SCER : PMC_SCDR, clk->pmc_mask);
  206         if (on)
  207                 while ((RD4(sc, PMC_SCSR) & clk->pmc_mask) != clk->pmc_mask)
  208                         continue;
  209         else
  210                 while ((RD4(sc, PMC_SCSR) & clk->pmc_mask) == clk->pmc_mask)
  211                         continue;
  212 }
  213 
  214 static void
  215 at91_pmc_set_periph_mode(struct at91_pmc_clock *clk, int on)
  216 {
  217         struct at91_pmc_softc *sc = pmc_softc;
  218 
  219         WR4(sc, on ? PMC_PCER : PMC_PCDR, clk->pmc_mask);
  220         if (on)
  221                 while ((RD4(sc, PMC_PCSR) & clk->pmc_mask) != clk->pmc_mask)
  222                         continue;
  223         else
  224                 while ((RD4(sc, PMC_PCSR) & clk->pmc_mask) == clk->pmc_mask)
  225                         continue;
  226 }
  227 
  228 struct at91_pmc_clock *
  229 at91_pmc_clock_add(const char *name, uint32_t irq, struct at91_pmc_clock *parent)
  230 {
  231         struct at91_pmc_clock *clk;
  232         int i, buflen;
  233 
  234         clk = malloc(sizeof(*clk), M_PMC, M_NOWAIT | M_ZERO);
  235         if (clk == NULL) 
  236                 goto err;
  237 
  238         buflen = strlen(name) + 1;
  239         clk->name = malloc(buflen, M_PMC, M_NOWAIT);
  240         if (clk->name == NULL) 
  241                 goto err;
  242 
  243         strlcpy(clk->name, name, buflen);
  244         clk->pmc_mask = 1 << irq;
  245         clk->set_mode = &at91_pmc_set_periph_mode;
  246         if (parent == NULL)
  247                 clk->parent = &mck;
  248         else
  249                 clk->parent = parent;
  250 
  251         for (i = 0; i < sizeof(clock_list) / sizeof(clock_list[0]); i++) {
  252                 if (clock_list[i] == NULL) {
  253                         clock_list[i] = clk;
  254                         return (clk);
  255                 }
  256         }
  257 err:
  258         if (clk != NULL) {
  259                 if (clk->name != NULL) 
  260                         free(clk->name, M_PMC);
  261                 free(clk, M_PMC);
  262         }
  263 
  264         panic("could not allocate pmc clock '%s'", name);
  265         return (NULL);
  266 }
  267 
  268 static void
  269 at91_pmc_clock_alias(const char *name, const char *alias)
  270 {
  271         struct at91_pmc_clock *clk, *alias_clk;
  272 
  273         clk = at91_pmc_clock_ref(name);
  274         if (clk)
  275                 alias_clk = at91_pmc_clock_add(alias, 0, clk->parent);
  276 
  277         if (clk && alias_clk) {
  278                 alias_clk->hz = clk->hz;
  279                 alias_clk->pmc_mask = clk->pmc_mask;
  280                 alias_clk->set_mode = clk->set_mode;
  281         }
  282 }
  283 
  284 struct at91_pmc_clock *
  285 at91_pmc_clock_ref(const char *name)
  286 {
  287         int i;
  288 
  289         for (i = 0; i < sizeof(clock_list) / sizeof(clock_list[0]); i++) {
  290                 if (clock_list[i] == NULL)
  291                     break;
  292                 if (strcmp(name, clock_list[i]->name) == 0)
  293                         return (clock_list[i]);
  294         }
  295 
  296         //printf("at91_pmc: Warning - did not find clock '%s'", name);
  297         return (NULL);
  298 }
  299 
  300 void
  301 at91_pmc_clock_deref(struct at91_pmc_clock *clk)
  302 {
  303 }
  304 
  305 void
  306 at91_pmc_clock_enable(struct at91_pmc_clock *clk)
  307 {
  308         /* XXX LOCKING? XXX */
  309         if (clk->parent)
  310                 at91_pmc_clock_enable(clk->parent);
  311         if (clk->refcnt++ == 0 && clk->set_mode)
  312                 clk->set_mode(clk, 1);
  313 }
  314 
  315 void
  316 at91_pmc_clock_disable(struct at91_pmc_clock *clk)
  317 {
  318         /* XXX LOCKING? XXX */
  319         if (--clk->refcnt == 0 && clk->set_mode)
  320                 clk->set_mode(clk, 0);
  321         if (clk->parent)
  322                 at91_pmc_clock_disable(clk->parent);
  323 }
  324 
  325 static int
  326 at91_pmc_pll_rate(struct at91_pmc_clock *clk, uint32_t reg)
  327 {
  328         uint32_t mul, div, freq;
  329 
  330         freq = clk->parent->hz;
  331         div = (reg >> clk->pll_div_shift) & clk->pll_div_mask;
  332         mul = (reg >> clk->pll_mul_shift) & clk->pll_mul_mask;
  333 
  334 //      printf("pll = (%d /  %d) * %d = %d\n",
  335 //          freq, div ,mul + 1, (freq/div) * (mul+1));
  336 
  337         if (div != 0 && mul != 0) {
  338                 freq /= div;
  339                 freq *= mul + 1;
  340         } else {
  341                 freq = 0;
  342         }
  343         clk->hz = freq;
  344 
  345 
  346         return (freq);
  347 }
  348 
  349 static uint32_t
  350 at91_pmc_pll_calc(struct at91_pmc_clock *clk, uint32_t out_freq)
  351 {
  352         uint32_t i, div = 0, mul = 0, diff = 1 << 30;
  353 
  354         unsigned ret = 0x3e00;
  355 
  356         if (out_freq > clk->pll_max_out)
  357                 goto fail;
  358 
  359         for (i = 1; i < 256; i++) {
  360                 int32_t diff1;
  361                 uint32_t input, mul1;
  362 
  363                 input = clk->parent->hz / i;
  364                 if (input < clk->pll_min_in)
  365                         break;
  366                 if (input > clk->pll_max_in)
  367                         continue;
  368 
  369                 mul1 = out_freq / input;
  370                 if (mul1 > (clk->pll_mul_mask + 1))
  371                         continue;
  372                 if (mul1 == 0)
  373                         break;
  374 
  375                 diff1 = out_freq - input * mul1;
  376                 if (diff1 < 0)
  377                         diff1 = -diff1;
  378                 if (diff > diff1) {
  379                         diff = diff1;
  380                         div = i;
  381                         mul = mul1;
  382                         if (diff == 0)
  383                                 break;
  384                 }
  385         }
  386         if (diff > (out_freq >> PMC_PLL_SHIFT_TOL))
  387                 goto fail;
  388 
  389         if (clk->set_outb != NULL)
  390                 ret |= clk->set_outb(out_freq);
  391 
  392         return (ret |
  393                 ((mul - 1) << clk->pll_mul_shift) |
  394                 (div << clk->pll_div_shift));
  395 fail:
  396         return (0);
  397 }
  398 
  399 static void
  400 at91_pmc_init_clock(struct at91_pmc_softc *sc, unsigned int main_clock)
  401 {
  402         uint32_t mckr;
  403         uint32_t mdiv;
  404 
  405         if (at91_is_sam9()) {
  406                 uhpck.pmc_mask = PMC_SCER_UHP_SAM9;
  407                 udpck.pmc_mask = PMC_SCER_UDP_SAM9;
  408         }
  409         mckr = RD4(sc, PMC_MCKR);
  410         sc->main_clock_hz = main_clock;
  411         main_ck.hz = main_clock;
  412 
  413         at91_pmc_pll_rate(&plla, RD4(sc, CKGR_PLLAR));
  414 
  415         if (at91_cpu_is(AT91_CPU_SAM9G45) && (mckr & PMC_MCKR_PLLADIV2))
  416                 plla.hz /= 2;
  417 
  418         /*
  419          * Initialize the usb clock.  This sets up pllb, but disables the
  420          * actual clock.
  421          */
  422         sc->pllb_init = at91_pmc_pll_calc(&pllb, 48000000 * 2) | 0x10000000;
  423         at91_pmc_pll_rate(&pllb, sc->pllb_init);
  424 
  425 #if 0
  426         /* Turn off USB clocks */
  427         at91_pmc_set_periph_mode(&ohci_clk, 0);
  428         at91_pmc_set_periph_mode(&udc_clk, 0);
  429 #endif
  430 
  431         if (at91_is_rm92()) {
  432                 WR4(sc, PMC_SCDR, PMC_SCER_UHP | PMC_SCER_UDP);
  433                 WR4(sc, PMC_SCER, PMC_SCER_MCKUDP);
  434         } else {
  435                 WR4(sc, PMC_SCDR, PMC_SCER_UHP_SAM9 | PMC_SCER_UDP_SAM9);
  436         }
  437         WR4(sc, CKGR_PLLBR, 0);
  438 
  439         /*
  440          * MCK and PCU derive from one of the primary clocks.  Initialize
  441          * this relationship.
  442          */
  443         mck.parent = clock_list[mckr & 0x3];
  444         mck.parent->refcnt++;
  445 
  446         cpu.hz = 
  447         mck.hz = mck.parent->hz /
  448              (1 << ((mckr & PMC_MCKR_PRES_MASK) >> 2));
  449 
  450         mdiv = (mckr & PMC_MCKR_MDIV_MASK) >> 8;
  451         if (at91_is_sam9()) {
  452                 if (mdiv > 0)
  453                         mck.hz /= mdiv * 2;
  454         } else 
  455                 mck.hz /= (1 + mdiv);
  456 
  457         /* Only found on SAM9G20 */
  458         if (at91_cpu_is(AT91_CPU_SAM9G20))
  459                 cpu.hz /= (mckr & PMC_MCKR_PDIV) ?  2 : 1;
  460 
  461         at91_master_clock = mck.hz;
  462 
  463         device_printf(sc->dev,
  464             "Primary: %d Hz PLLA: %d MHz CPU: %d MHz MCK: %d MHz\n",
  465             sc->main_clock_hz,
  466             plla.hz / 1000000,
  467             cpu.hz / 1000000, mck.hz / 1000000);
  468 
  469         /* Turn off "Progamable" clocks */
  470         WR4(sc, PMC_SCDR, PMC_SCER_PCK0 | PMC_SCER_PCK1 | PMC_SCER_PCK2 |
  471             PMC_SCER_PCK3);
  472 
  473         /* XXX kludge, turn on all peripherals */
  474         WR4(sc, PMC_PCER, 0xffffffff);
  475 
  476         /* Disable all interrupts for PMC */
  477         WR4(sc, PMC_IDR, 0xffffffff);
  478 }
  479 
  480 static void
  481 at91_pmc_deactivate(device_t dev)
  482 {
  483         struct at91_pmc_softc *sc;
  484 
  485         sc = device_get_softc(dev);
  486         bus_generic_detach(sc->dev);
  487         if (sc->mem_res)
  488                 bus_release_resource(dev, SYS_RES_IOPORT,
  489                     rman_get_rid(sc->mem_res), sc->mem_res);
  490         sc->mem_res = 0;
  491         return;
  492 }
  493 
  494 static int
  495 at91_pmc_activate(device_t dev)
  496 {
  497         struct at91_pmc_softc *sc;
  498         int rid;
  499 
  500         sc = device_get_softc(dev);
  501         rid = 0;
  502         sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
  503             RF_ACTIVE);
  504         if (sc->mem_res == NULL)
  505                 goto errout;
  506         return (0);
  507 errout:
  508         at91_pmc_deactivate(dev);
  509         return (ENOMEM);
  510 }
  511 
  512 static int
  513 at91_pmc_probe(device_t dev)
  514 {
  515 
  516         device_set_desc(dev, "PMC");
  517         return (0);
  518 }
  519 
  520 #if !defined(AT91C_MAIN_CLOCK)
  521 static unsigned int
  522 at91_pmc_sense_mainf(struct at91_pmc_softc *sc)
  523 {
  524         unsigned int ckgr_val;
  525         unsigned int diff, matchdiff;
  526         int i, match;
  527 
  528         ckgr_val = (RD4(sc, CKGR_MCFR) & CKGR_MCFR_MAINF_MASK) << 11;
  529 
  530         /*
  531          * Try to find the standard frequency that match best.
  532          */
  533         match = 0;
  534         matchdiff = abs(ckgr_val - at91_mainf_tbl[0]);
  535         for (i = 1; i < MAINF_TBL_LEN; i++) {
  536                 diff = abs(ckgr_val - at91_mainf_tbl[i]);
  537                 if (diff < matchdiff) {
  538                         match = i;
  539                         matchdiff = diff;
  540                 }
  541         }
  542         return (at91_mainf_tbl[match]);
  543 }
  544 #endif
  545 
  546 static int
  547 at91_pmc_attach(device_t dev)
  548 {
  549         unsigned int mainf;
  550         int err;
  551 
  552         pmc_softc = device_get_softc(dev);
  553         pmc_softc->dev = dev;
  554         if ((err = at91_pmc_activate(dev)) != 0)
  555                 return (err);
  556 
  557         /*
  558          * Configure main clock frequency.
  559          */
  560 #if !defined(AT91C_MAIN_CLOCK)
  561         mainf = at91_pmc_sense_mainf(pmc_softc);
  562 #else
  563         mainf = AT91C_MAIN_CLOCK;
  564 #endif
  565         at91_pmc_init_clock(pmc_softc, mainf);
  566 
  567         /* These clocks refrenced by "special" names */
  568         at91_pmc_clock_alias("ohci0", "ohci_clk");
  569         at91_pmc_clock_alias("udp0",  "udp_clk");
  570 
  571         return (0);
  572 }
  573 
  574 static device_method_t at91_pmc_methods[] = {
  575         DEVMETHOD(device_probe, at91_pmc_probe),
  576         DEVMETHOD(device_attach, at91_pmc_attach),
  577         {0, 0},
  578 };
  579 
  580 static driver_t at91_pmc_driver = {
  581         "at91_pmc",
  582         at91_pmc_methods,
  583         sizeof(struct at91_pmc_softc),
  584 };
  585 static devclass_t at91_pmc_devclass;
  586 
  587 DRIVER_MODULE(at91_pmc, atmelarm, at91_pmc_driver, at91_pmc_devclass, 0, 0);

Cache object: afa703d7bf709aebbe692f78ca4f7fa8


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