Merge commit '4d3a7b124e08a597d5f01fb2a71f3a4677a360a9'
[osmocom-bb.git] / src / target / firmware / calypso / clock.c
1 /* Driver for Calypso clock management */
2
3 /* (C) 2010 by Harald Welte <laforge@gnumonks.org>
4  *
5  * All Rights Reserved
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  */
22
23 #include <stdint.h>
24 #include <stdio.h>
25
26 //#define DEBUG
27 #include <debug.h>
28
29 #include <memory.h>
30 #include <calypso/clock.h>
31
32 #define REG_DPLL                0xffff9800
33 #define DPLL_LOCK               (1 << 0)
34 #define DPLL_BREAKLN            (1 << 1)
35 #define DPLL_BYPASS_DIV_SHIFT   2               /* 2 bits */
36 #define DPLL_PLL_ENABLE         (1 << 4)
37 #define DPLL_PLL_DIV_SHIFT      5               /* 2 bits */
38 #define DPLL_PLL_MULT_SHIFT     7               /* 5 bits */
39 #define DPLL_TEST               (1 << 12)
40 #define DPLL_IOB                (1 << 13)       /* Initialize on break */
41 #define DPLL_IAI                (1 << 14)       /* Initialize after Idle */
42
43 #define BASE_ADDR_CLKM  0xfffffd00
44 #define CLKM_REG(m)     (BASE_ADDR_CLKM+(m))
45
46 enum clkm_reg {
47         CNTL_ARM_CLK    = 0,
48         CNTL_CLK        = 2,
49         CNTL_RST        = 4,
50         CNTL_ARM_DIV    = 8,
51 };
52
53 /* CNTL_ARM_CLK */
54 #define ARM_CLK_BIG_SLEEP       (1 << 0)        /* MCU Master Clock enabled? */
55 #define ARM_CLK_CLKIN_SEL0      (1 << 1)        /* MCU source clock (0 = DPLL output, 1 = VTCXO or CLKIN */
56 #define ARM_CLK_CLKIN_SEL       (1 << 2)        /* 0 = VTCXO or 1 = CLKIN */
57 #define ARM_CLK_MCLK_DIV5       (1 << 3)        /* enable 1.5 or 2.5 division factor */
58 #define ARM_CLK_MCLK_DIV_SHIFT  4               /* 3 bits */
59 #define ARM_CLK_DEEP_POWER_SHIFT        8
60 #define ARM_CLK_DEEP_SLEEP      12
61
62 /* CNTL_CLK */
63 #define CLK_IRQ_CLK_DIS         (1 << 0)        /* IRQ clock control (0 always, 1 according ARM_MCLK_EN) */
64 #define CLK_BRIDGE_CLK_DIS      (1 << 1)
65 #define CLK_TIMER_CLK_DIS       (1 << 2)
66 #define CLK_DPLL_DIS            (1 << 3)        /* 0: DPLL is not stopped during SLEEP */
67 #define CLK_CLKOUT_EN           (1 << 4)        /* Enable CLKOUT output pins */
68 #define CLK_EN_IDLE3_FLG        (1 << 5)        /* DSP idle flag control (1 =
69                                                  * SAM/HOM register forced to HOM when DSP IDLE3) */
70 #define CLK_VCLKOUT_DIV2        (1 << 6)        /* 1: VCLKOUT-FR is divided by 2 */
71 #define CLK_VTCXO_DIV2          (1 << 7)        /* 1: VTCXO is dividied by 2 */
72
73 #define BASE_ADDR_MEMIF         0xfffffb00
74 #define MEMIF_REG(x)            (BASE_ADDR_MEMIF+(x))
75
76 enum memif_reg {
77         API_RHEA_CTL    = 0x0e,
78         EXTRA_CONF      = 0x10,
79 };
80
81 static void dump_reg16(uint32_t addr, char *name)
82 {
83         printf("%s=0x%04x\n", name, readw(addr));
84 }
85
86 void calypso_clk_dump(void)
87 {
88         dump_reg16(REG_DPLL, "REG_DPLL");
89         dump_reg16(CLKM_REG(CNTL_ARM_CLK), "CNTL_ARM_CLK");
90         dump_reg16(CLKM_REG(CNTL_CLK), "CNTL_CLK");
91         dump_reg16(CLKM_REG(CNTL_RST), "CNTL_RST");
92         dump_reg16(CLKM_REG(CNTL_ARM_DIV), "CNTL_ARM_DIV");
93 }
94
95 void calypso_pll_set(uint16_t inp)
96 {
97         uint8_t mult = inp >> 8;
98         uint8_t div = inp & 0xff;
99         uint16_t reg = readw(REG_DPLL);
100
101         reg &= ~0x0fe0;
102         reg |= (div & 0x3) << DPLL_PLL_DIV_SHIFT;
103         reg |= (mult & 0x1f) << DPLL_PLL_MULT_SHIFT;
104         reg |= DPLL_PLL_ENABLE;
105
106         writew(reg, REG_DPLL);
107 }
108
109 void calypso_reset_set(enum calypso_rst calypso_rst, int active)
110 {
111         uint8_t reg = readb(CLKM_REG(CNTL_RST));
112
113         if (active)
114                 reg |= calypso_rst;
115         else
116                 reg &= ~calypso_rst;
117
118         writeb(reg, CLKM_REG(CNTL_RST));
119 }
120
121 int calypso_reset_get(enum calypso_rst calypso_rst)
122 {
123         uint8_t reg = readb(CLKM_REG(CNTL_RST));
124
125         if (reg & calypso_rst)
126                 return 1;
127         else
128                 return 0;
129 }
130
131 void calypso_clock_set(uint8_t vtcxo_div2, uint16_t inp, enum mclk_div mclk_div)
132 {
133         uint16_t cntl_clock = readw(CLKM_REG(CNTL_CLK));
134         uint16_t cntl_arm_clk = readw(CLKM_REG(CNTL_ARM_CLK));
135
136         /* First set the vtcxo_div2 */
137         cntl_clock &= ~CLK_VCLKOUT_DIV2;
138         if (vtcxo_div2)
139                 cntl_clock |= CLK_VTCXO_DIV2;
140         else
141                 cntl_clock &= ~CLK_VTCXO_DIV2;
142         writew(cntl_clock, CLKM_REG(CNTL_CLK));
143
144         /* Then configure the MCLK divider */
145         cntl_arm_clk &= ~ARM_CLK_CLKIN_SEL0;
146         if (mclk_div & 0x80) {
147                 mclk_div &= ~0x80;
148                 cntl_arm_clk |= ARM_CLK_MCLK_DIV5;
149         } else
150                 cntl_arm_clk &= ~ARM_CLK_MCLK_DIV5;
151         cntl_arm_clk &= ~(0x7 << ARM_CLK_MCLK_DIV_SHIFT);
152         cntl_arm_clk |= (mclk_div << ARM_CLK_MCLK_DIV_SHIFT);
153         writew(cntl_arm_clk, CLKM_REG(CNTL_ARM_CLK));
154
155         /* Then finally set the PLL */
156         calypso_pll_set(inp);
157 }
158
159 void calypso_mem_cfg(enum calypso_bank bank, uint8_t ws,
160                      enum calypso_mem_width width, int we)
161 {
162         writew((ws & 0x1f) | ((width & 3) << 5) | ((we & 1) << 7),
163                BASE_ADDR_MEMIF + bank);
164 }
165
166 void calypso_bootrom(int enable)
167 {
168         uint16_t conf = readw(MEMIF_REG(EXTRA_CONF));
169
170         conf |= (3 << 8);
171
172         if (enable)
173                 conf &= ~(1 << 9);
174
175         writew(conf, MEMIF_REG(EXTRA_CONF));
176 }
177
178 void calypso_debugunit(int enable)
179 {
180         uint16_t conf = readw(MEMIF_REG(EXTRA_CONF));
181
182         if (enable)
183                 conf &= ~(1 << 11);
184         else
185                 conf |= (1 << 11);
186
187         writew(conf, MEMIF_REG(EXTRA_CONF));
188 }
189
190 #define REG_RHEA_CNTL   0xfffff900
191 #define REG_API_CNTL    0xfffff902
192 #define REG_ARM_RHEA    0xfffff904
193
194 void calypso_rhea_cfg(uint8_t fac0, uint8_t fac1, uint8_t timeout,
195                       uint8_t ws_h, uint8_t ws_l, uint8_t w_en0, uint8_t w_en1)
196 {
197         writew(fac0 | (fac1 << 4) | (timeout << 8), REG_RHEA_CNTL);
198         writew(ws_h | (ws_l << 5), REG_API_CNTL);
199         writew(w_en0 | (w_en1 << 1), REG_ARM_RHEA);
200 }