1 /* $Id: at91pmc.c,v 1.2 2008/07/03 01:15:38 matt Exp $ */ 2 /* $NetBSD: at91pmc.c,v 1.2 2008/07/03 01:15:38 matt Exp $ */ 3 4 /* 5 * Copyright (c) 2007 Embedtronics Oy 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the NetBSD 19 * Foundation, Inc. and its contributors. 20 * 4. Neither the name of The NetBSD Foundation nor the names of its 21 * contributors may be used to endorse or promote products derived 22 * from this software without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 25 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 26 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 27 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 28 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 * POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37 #include <sys/cdefs.h> 38 __KERNEL_RCSID(0, "$NetBSD"); 39 40 #include <sys/types.h> 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 #include <sys/kernel.h> 44 #include <sys/time.h> 45 #include <sys/device.h> 46 47 #include <machine/bus.h> 48 #include <machine/intr.h> 49 50 #include <arm/cpufunc.h> 51 52 #include <arm/at91/at91reg.h> 53 #include <arm/at91/at91var.h> 54 #include <arm/at91/at91pmcreg.h> 55 #include <arm/at91/at91pmcvar.h> 56 57 #define SLOW_CLOCK 32768LU 58 59 void 60 at91pmc_get_clocks(struct at91bus_clocks *clocks) 61 { 62 u_int64_t mclk, pllaclk, pllbclk, pclk, mstclk; 63 u_int32_t reg; 64 65 if (!((reg = PMCREG(PMC_MOR)) & PMC_MOR_MOSCEN)) 66 panic("%s: main oscillator not enabled (MOR=0x%#X)", __FUNCTION__, reg); 67 68 if (!((reg = PMCREG(PMC_MCFR)) & PMC_MCFR_MAINRDY)) 69 panic("%s: main oscillator not ready (MCFR=0x%#X)", __FUNCTION__, reg); 70 71 mclk = ((reg & PMC_MCFR_MAINF) * SLOW_CLOCK) / 16U; 72 73 // try to guess some nice MHz value 74 if (((mclk / 1000) % 1000) >= 990) { 75 mclk += 1000000U - (mclk % 1000000U); 76 } else if (((mclk / 1000) % 1000) <= 10) { 77 mclk -= (mclk % 1000000U); 78 } 79 80 reg = PMCREG(PMC_PLLAR); pllaclk = 0; 81 if (reg & PMC_PLL_DIV) { 82 pllaclk = mclk * (((reg & PMC_PLL_MUL) >> PMC_PLL_MUL_SHIFT) + 1); 83 pllaclk /= (reg & PMC_PLL_DIV) >> PMC_PLL_DIV_SHIFT; 84 } 85 86 reg = PMCREG(PMC_PLLBR); pllbclk = 0; 87 if (reg & PMC_PLL_DIV) { 88 pllbclk = mclk * (((reg & PMC_PLL_MUL) >> PMC_PLL_MUL_SHIFT) + 1); 89 pllbclk /= (reg & PMC_PLL_DIV) >> PMC_PLL_DIV_SHIFT; 90 if (reg & PMC_PLLBR_USB_96M) { 91 pllbclk /= 2; 92 } 93 } 94 95 reg = PMCREG(PMC_MCKR); 96 switch ((reg & PMC_MCKR_CSS)) { 97 case PMC_MCKR_CSS_SLOW_CLK: 98 pclk = SLOW_CLOCK; 99 break; 100 default: 101 case PMC_MCKR_CSS_MAIN_CLK: 102 pclk = mclk; 103 break; 104 case PMC_MCKR_CSS_PLLA: 105 pclk = pllaclk; 106 break; 107 case PMC_MCKR_CSS_PLLB: 108 pclk = pllbclk; 109 break; 110 } 111 pclk >>= (reg & PMC_MCKR_PRES) >> PMC_MCKR_PRES_SHIFT; 112 mstclk = pclk / (((reg & PMC_MCKR_MDIV) >> PMC_MCKR_MDIV_SHIFT) + 1); 113 114 clocks->slow = SLOW_CLOCK; 115 clocks->main = mclk; 116 clocks->cpu = pclk; 117 clocks->master = mstclk; 118 clocks->plla = pllaclk; 119 clocks->pllb = pllbclk; 120 } 121 122 123 #define PID_COUNT 32 124 static int pid_enable_count[PID_COUNT] = {0}; 125 126 void 127 at91pmc_peripheral_clock(int pid, int enable) 128 { 129 int s; 130 131 if (pid < 0 || pid >= PID_COUNT) 132 panic("%s: pid %d out of range", __FUNCTION__, pid); 133 134 s = splhigh(); 135 136 if (enable) { 137 pid_enable_count[pid]++; 138 PMCREG(PMC_PCER) = (1U << pid); 139 } else { 140 if (--pid_enable_count[pid] < 0) 141 panic("%s: pid %d enable count got negative (%d)", 142 __FUNCTION__, pid, pid_enable_count[pid]); 143 if (pid_enable_count[pid] == 0) 144 PMCREG(PMC_PCDR) = (1U << pid); 145 } 146 147 splx(s); 148 149 if (enable) { 150 int c; 151 for (c = 0; c < 10000; c++) { 152 __insn_barrier(); 153 } 154 } 155 } 156