Branch data Line data Source code
1 : : /*
2 : : * Sonics Silicon Backplane
3 : : * Broadcom ChipCommon Power Management Unit driver
4 : : *
5 : : * Copyright 2009, Michael Buesch <m@bues.ch>
6 : : * Copyright 2007, Broadcom Corporation
7 : : *
8 : : * Licensed under the GNU/GPL. See COPYING for details.
9 : : */
10 : :
11 : : #include "ssb_private.h"
12 : :
13 : : #include <linux/ssb/ssb.h>
14 : : #include <linux/ssb/ssb_regs.h>
15 : : #include <linux/ssb/ssb_driver_chipcommon.h>
16 : : #include <linux/delay.h>
17 : : #include <linux/export.h>
18 : : #ifdef CONFIG_BCM47XX
19 : : #include <linux/bcm47xx_nvram.h>
20 : : #endif
21 : :
22 : 0 : static u32 ssb_chipco_pll_read(struct ssb_chipcommon *cc, u32 offset)
23 : : {
24 : 0 : chipco_write32(cc, SSB_CHIPCO_PLLCTL_ADDR, offset);
25 : 0 : return chipco_read32(cc, SSB_CHIPCO_PLLCTL_DATA);
26 : : }
27 : :
28 : 0 : static void ssb_chipco_pll_write(struct ssb_chipcommon *cc,
29 : : u32 offset, u32 value)
30 : : {
31 : 0 : chipco_write32(cc, SSB_CHIPCO_PLLCTL_ADDR, offset);
32 : 0 : chipco_write32(cc, SSB_CHIPCO_PLLCTL_DATA, value);
33 : 0 : }
34 : :
35 : : static void ssb_chipco_regctl_maskset(struct ssb_chipcommon *cc,
36 : : u32 offset, u32 mask, u32 set)
37 : : {
38 : : u32 value;
39 : :
40 : : chipco_read32(cc, SSB_CHIPCO_REGCTL_ADDR);
41 : : chipco_write32(cc, SSB_CHIPCO_REGCTL_ADDR, offset);
42 : : chipco_read32(cc, SSB_CHIPCO_REGCTL_ADDR);
43 : : value = chipco_read32(cc, SSB_CHIPCO_REGCTL_DATA);
44 : : value &= mask;
45 : : value |= set;
46 : : chipco_write32(cc, SSB_CHIPCO_REGCTL_DATA, value);
47 : : chipco_read32(cc, SSB_CHIPCO_REGCTL_DATA);
48 : : }
49 : :
50 : : struct pmu0_plltab_entry {
51 : : u16 freq; /* Crystal frequency in kHz.*/
52 : : u8 xf; /* Crystal frequency value for PMU control */
53 : : u8 wb_int;
54 : : u32 wb_frac;
55 : : };
56 : :
57 : : static const struct pmu0_plltab_entry pmu0_plltab[] = {
58 : : { .freq = 12000, .xf = 1, .wb_int = 73, .wb_frac = 349525, },
59 : : { .freq = 13000, .xf = 2, .wb_int = 67, .wb_frac = 725937, },
60 : : { .freq = 14400, .xf = 3, .wb_int = 61, .wb_frac = 116508, },
61 : : { .freq = 15360, .xf = 4, .wb_int = 57, .wb_frac = 305834, },
62 : : { .freq = 16200, .xf = 5, .wb_int = 54, .wb_frac = 336579, },
63 : : { .freq = 16800, .xf = 6, .wb_int = 52, .wb_frac = 399457, },
64 : : { .freq = 19200, .xf = 7, .wb_int = 45, .wb_frac = 873813, },
65 : : { .freq = 19800, .xf = 8, .wb_int = 44, .wb_frac = 466033, },
66 : : { .freq = 20000, .xf = 9, .wb_int = 44, .wb_frac = 0, },
67 : : { .freq = 25000, .xf = 10, .wb_int = 70, .wb_frac = 419430, },
68 : : { .freq = 26000, .xf = 11, .wb_int = 67, .wb_frac = 725937, },
69 : : { .freq = 30000, .xf = 12, .wb_int = 58, .wb_frac = 699050, },
70 : : { .freq = 38400, .xf = 13, .wb_int = 45, .wb_frac = 873813, },
71 : : { .freq = 40000, .xf = 14, .wb_int = 45, .wb_frac = 0, },
72 : : };
73 : : #define SSB_PMU0_DEFAULT_XTALFREQ 20000
74 : :
75 : : static const struct pmu0_plltab_entry * pmu0_plltab_find_entry(u32 crystalfreq)
76 : : {
77 : : const struct pmu0_plltab_entry *e;
78 : : unsigned int i;
79 : :
80 [ # # # # ]: 0 : for (i = 0; i < ARRAY_SIZE(pmu0_plltab); i++) {
81 : 0 : e = &pmu0_plltab[i];
82 [ # # # # ]: 0 : if (e->freq == crystalfreq)
83 : : return e;
84 : : }
85 : :
86 : : return NULL;
87 : : }
88 : :
89 : : /* Tune the PLL to the crystal speed. crystalfreq is in kHz. */
90 : 0 : static void ssb_pmu0_pllinit_r0(struct ssb_chipcommon *cc,
91 : : u32 crystalfreq)
92 : : {
93 : 0 : struct ssb_bus *bus = cc->dev->bus;
94 : 0 : const struct pmu0_plltab_entry *e = NULL;
95 : 0 : u32 pmuctl, tmp, pllctl;
96 : 0 : unsigned int i;
97 : :
98 [ # # ]: 0 : if (crystalfreq)
99 : : e = pmu0_plltab_find_entry(crystalfreq);
100 [ # # ]: 0 : if (!e)
101 : : e = pmu0_plltab_find_entry(SSB_PMU0_DEFAULT_XTALFREQ);
102 [ # # ]: 0 : BUG_ON(!e);
103 : 0 : crystalfreq = e->freq;
104 : 0 : cc->pmu.crystalfreq = e->freq;
105 : :
106 : : /* Check if the PLL already is programmed to this frequency. */
107 : 0 : pmuctl = chipco_read32(cc, SSB_CHIPCO_PMU_CTL);
108 [ # # ]: 0 : if (((pmuctl & SSB_CHIPCO_PMU_CTL_XTALFREQ) >> SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT) == e->xf) {
109 : : /* We're already there... */
110 : : return;
111 : : }
112 : :
113 : 0 : dev_info(cc->dev->dev, "Programming PLL to %u.%03u MHz\n",
114 : : crystalfreq / 1000, crystalfreq % 1000);
115 : :
116 : : /* First turn the PLL off. */
117 [ # # # ]: 0 : switch (bus->chip_id) {
118 : 0 : case 0x4328:
119 : 0 : chipco_mask32(cc, SSB_CHIPCO_PMU_MINRES_MSK,
120 : : ~(1 << SSB_PMURES_4328_BB_PLL_PU));
121 : 0 : chipco_mask32(cc, SSB_CHIPCO_PMU_MAXRES_MSK,
122 : : ~(1 << SSB_PMURES_4328_BB_PLL_PU));
123 : : break;
124 : 0 : case 0x5354:
125 : 0 : chipco_mask32(cc, SSB_CHIPCO_PMU_MINRES_MSK,
126 : : ~(1 << SSB_PMURES_5354_BB_PLL_PU));
127 : 0 : chipco_mask32(cc, SSB_CHIPCO_PMU_MAXRES_MSK,
128 : : ~(1 << SSB_PMURES_5354_BB_PLL_PU));
129 : : break;
130 : : default:
131 : 0 : WARN_ON(1);
132 : : }
133 [ # # ]: 0 : for (i = 1500; i; i--) {
134 : 0 : tmp = chipco_read32(cc, SSB_CHIPCO_CLKCTLST);
135 [ # # ]: 0 : if (!(tmp & SSB_CHIPCO_CLKCTLST_HAVEHT))
136 : : break;
137 : 0 : udelay(10);
138 : : }
139 : 0 : tmp = chipco_read32(cc, SSB_CHIPCO_CLKCTLST);
140 [ # # ]: 0 : if (tmp & SSB_CHIPCO_CLKCTLST_HAVEHT)
141 : 0 : dev_emerg(cc->dev->dev, "Failed to turn the PLL off!\n");
142 : :
143 : : /* Set PDIV in PLL control 0. */
144 : 0 : pllctl = ssb_chipco_pll_read(cc, SSB_PMU0_PLLCTL0);
145 [ # # ]: 0 : if (crystalfreq >= SSB_PMU0_PLLCTL0_PDIV_FREQ)
146 : 0 : pllctl |= SSB_PMU0_PLLCTL0_PDIV_MSK;
147 : : else
148 : 0 : pllctl &= ~SSB_PMU0_PLLCTL0_PDIV_MSK;
149 : 0 : ssb_chipco_pll_write(cc, SSB_PMU0_PLLCTL0, pllctl);
150 : :
151 : : /* Set WILD in PLL control 1. */
152 : 0 : pllctl = ssb_chipco_pll_read(cc, SSB_PMU0_PLLCTL1);
153 : 0 : pllctl &= ~SSB_PMU0_PLLCTL1_STOPMOD;
154 : 0 : pllctl &= ~(SSB_PMU0_PLLCTL1_WILD_IMSK | SSB_PMU0_PLLCTL1_WILD_FMSK);
155 : 0 : pllctl |= ((u32)e->wb_int << SSB_PMU0_PLLCTL1_WILD_IMSK_SHIFT) & SSB_PMU0_PLLCTL1_WILD_IMSK;
156 : 0 : pllctl |= ((u32)e->wb_frac << SSB_PMU0_PLLCTL1_WILD_FMSK_SHIFT) & SSB_PMU0_PLLCTL1_WILD_FMSK;
157 [ # # ]: 0 : if (e->wb_frac == 0)
158 : 0 : pllctl |= SSB_PMU0_PLLCTL1_STOPMOD;
159 : 0 : ssb_chipco_pll_write(cc, SSB_PMU0_PLLCTL1, pllctl);
160 : :
161 : : /* Set WILD in PLL control 2. */
162 : 0 : pllctl = ssb_chipco_pll_read(cc, SSB_PMU0_PLLCTL2);
163 : 0 : pllctl &= ~SSB_PMU0_PLLCTL2_WILD_IMSKHI;
164 : 0 : pllctl |= (((u32)e->wb_int >> 4) << SSB_PMU0_PLLCTL2_WILD_IMSKHI_SHIFT) & SSB_PMU0_PLLCTL2_WILD_IMSKHI;
165 : 0 : ssb_chipco_pll_write(cc, SSB_PMU0_PLLCTL2, pllctl);
166 : :
167 : : /* Set the crystalfrequency and the divisor. */
168 : 0 : pmuctl = chipco_read32(cc, SSB_CHIPCO_PMU_CTL);
169 : 0 : pmuctl &= ~SSB_CHIPCO_PMU_CTL_ILP_DIV;
170 : 0 : pmuctl |= (((crystalfreq + 127) / 128 - 1) << SSB_CHIPCO_PMU_CTL_ILP_DIV_SHIFT)
171 : 0 : & SSB_CHIPCO_PMU_CTL_ILP_DIV;
172 : 0 : pmuctl &= ~SSB_CHIPCO_PMU_CTL_XTALFREQ;
173 : 0 : pmuctl |= ((u32)e->xf << SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT) & SSB_CHIPCO_PMU_CTL_XTALFREQ;
174 : 0 : chipco_write32(cc, SSB_CHIPCO_PMU_CTL, pmuctl);
175 : : }
176 : :
177 : : struct pmu1_plltab_entry {
178 : : u16 freq; /* Crystal frequency in kHz.*/
179 : : u8 xf; /* Crystal frequency value for PMU control */
180 : : u8 ndiv_int;
181 : : u32 ndiv_frac;
182 : : u8 p1div;
183 : : u8 p2div;
184 : : };
185 : :
186 : : static const struct pmu1_plltab_entry pmu1_plltab[] = {
187 : : { .freq = 12000, .xf = 1, .p1div = 3, .p2div = 22, .ndiv_int = 0x9, .ndiv_frac = 0xFFFFEF, },
188 : : { .freq = 13000, .xf = 2, .p1div = 1, .p2div = 6, .ndiv_int = 0xb, .ndiv_frac = 0x483483, },
189 : : { .freq = 14400, .xf = 3, .p1div = 1, .p2div = 10, .ndiv_int = 0xa, .ndiv_frac = 0x1C71C7, },
190 : : { .freq = 15360, .xf = 4, .p1div = 1, .p2div = 5, .ndiv_int = 0xb, .ndiv_frac = 0x755555, },
191 : : { .freq = 16200, .xf = 5, .p1div = 1, .p2div = 10, .ndiv_int = 0x5, .ndiv_frac = 0x6E9E06, },
192 : : { .freq = 16800, .xf = 6, .p1div = 1, .p2div = 10, .ndiv_int = 0x5, .ndiv_frac = 0x3CF3CF, },
193 : : { .freq = 19200, .xf = 7, .p1div = 1, .p2div = 9, .ndiv_int = 0x5, .ndiv_frac = 0x17B425, },
194 : : { .freq = 19800, .xf = 8, .p1div = 1, .p2div = 11, .ndiv_int = 0x4, .ndiv_frac = 0xA57EB, },
195 : : { .freq = 20000, .xf = 9, .p1div = 1, .p2div = 11, .ndiv_int = 0x4, .ndiv_frac = 0, },
196 : : { .freq = 24000, .xf = 10, .p1div = 3, .p2div = 11, .ndiv_int = 0xa, .ndiv_frac = 0, },
197 : : { .freq = 25000, .xf = 11, .p1div = 5, .p2div = 16, .ndiv_int = 0xb, .ndiv_frac = 0, },
198 : : { .freq = 26000, .xf = 12, .p1div = 1, .p2div = 2, .ndiv_int = 0x10, .ndiv_frac = 0xEC4EC4, },
199 : : { .freq = 30000, .xf = 13, .p1div = 3, .p2div = 8, .ndiv_int = 0xb, .ndiv_frac = 0, },
200 : : { .freq = 38400, .xf = 14, .p1div = 1, .p2div = 5, .ndiv_int = 0x4, .ndiv_frac = 0x955555, },
201 : : { .freq = 40000, .xf = 15, .p1div = 1, .p2div = 2, .ndiv_int = 0xb, .ndiv_frac = 0, },
202 : : };
203 : :
204 : : #define SSB_PMU1_DEFAULT_XTALFREQ 15360
205 : :
206 : : static const struct pmu1_plltab_entry * pmu1_plltab_find_entry(u32 crystalfreq)
207 : : {
208 : : const struct pmu1_plltab_entry *e;
209 : : unsigned int i;
210 : :
211 [ # # # # ]: 0 : for (i = 0; i < ARRAY_SIZE(pmu1_plltab); i++) {
212 : 0 : e = &pmu1_plltab[i];
213 [ # # # # ]: 0 : if (e->freq == crystalfreq)
214 : : return e;
215 : : }
216 : :
217 : : return NULL;
218 : : }
219 : :
220 : : /* Tune the PLL to the crystal speed. crystalfreq is in kHz. */
221 : 0 : static void ssb_pmu1_pllinit_r0(struct ssb_chipcommon *cc,
222 : : u32 crystalfreq)
223 : : {
224 : 0 : struct ssb_bus *bus = cc->dev->bus;
225 : 0 : const struct pmu1_plltab_entry *e = NULL;
226 : 0 : u32 buffer_strength = 0;
227 : 0 : u32 tmp, pllctl, pmuctl;
228 : 0 : unsigned int i;
229 : :
230 [ # # ]: 0 : if (bus->chip_id == 0x4312) {
231 : : /* We do not touch the BCM4312 PLL and assume
232 : : * the default crystal settings work out-of-the-box. */
233 : 0 : cc->pmu.crystalfreq = 20000;
234 : 0 : return;
235 : : }
236 : :
237 [ # # ]: 0 : if (crystalfreq)
238 : : e = pmu1_plltab_find_entry(crystalfreq);
239 [ # # ]: 0 : if (!e)
240 : : e = pmu1_plltab_find_entry(SSB_PMU1_DEFAULT_XTALFREQ);
241 [ # # ]: 0 : BUG_ON(!e);
242 : 0 : crystalfreq = e->freq;
243 : 0 : cc->pmu.crystalfreq = e->freq;
244 : :
245 : : /* Check if the PLL already is programmed to this frequency. */
246 : 0 : pmuctl = chipco_read32(cc, SSB_CHIPCO_PMU_CTL);
247 [ # # ]: 0 : if (((pmuctl & SSB_CHIPCO_PMU_CTL_XTALFREQ) >> SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT) == e->xf) {
248 : : /* We're already there... */
249 : : return;
250 : : }
251 : :
252 : 0 : dev_info(cc->dev->dev, "Programming PLL to %u.%03u MHz\n",
253 : : crystalfreq / 1000, crystalfreq % 1000);
254 : :
255 : : /* First turn the PLL off. */
256 [ # # ]: 0 : switch (bus->chip_id) {
257 : 0 : case 0x4325:
258 : 0 : chipco_mask32(cc, SSB_CHIPCO_PMU_MINRES_MSK,
259 : : ~((1 << SSB_PMURES_4325_BBPLL_PWRSW_PU) |
260 : : (1 << SSB_PMURES_4325_HT_AVAIL)));
261 : 0 : chipco_mask32(cc, SSB_CHIPCO_PMU_MAXRES_MSK,
262 : : ~((1 << SSB_PMURES_4325_BBPLL_PWRSW_PU) |
263 : : (1 << SSB_PMURES_4325_HT_AVAIL)));
264 : : /* Adjust the BBPLL to 2 on all channels later. */
265 : 0 : buffer_strength = 0x222222;
266 : 0 : break;
267 : : default:
268 : 0 : WARN_ON(1);
269 : : }
270 [ # # ]: 0 : for (i = 1500; i; i--) {
271 : 0 : tmp = chipco_read32(cc, SSB_CHIPCO_CLKCTLST);
272 [ # # ]: 0 : if (!(tmp & SSB_CHIPCO_CLKCTLST_HAVEHT))
273 : : break;
274 : 0 : udelay(10);
275 : : }
276 : 0 : tmp = chipco_read32(cc, SSB_CHIPCO_CLKCTLST);
277 [ # # ]: 0 : if (tmp & SSB_CHIPCO_CLKCTLST_HAVEHT)
278 : 0 : dev_emerg(cc->dev->dev, "Failed to turn the PLL off!\n");
279 : :
280 : : /* Set p1div and p2div. */
281 : 0 : pllctl = ssb_chipco_pll_read(cc, SSB_PMU1_PLLCTL0);
282 : 0 : pllctl &= ~(SSB_PMU1_PLLCTL0_P1DIV | SSB_PMU1_PLLCTL0_P2DIV);
283 : 0 : pllctl |= ((u32)e->p1div << SSB_PMU1_PLLCTL0_P1DIV_SHIFT) & SSB_PMU1_PLLCTL0_P1DIV;
284 : 0 : pllctl |= ((u32)e->p2div << SSB_PMU1_PLLCTL0_P2DIV_SHIFT) & SSB_PMU1_PLLCTL0_P2DIV;
285 : 0 : ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL0, pllctl);
286 : :
287 : : /* Set ndiv int and ndiv mode */
288 : 0 : pllctl = ssb_chipco_pll_read(cc, SSB_PMU1_PLLCTL2);
289 : 0 : pllctl &= ~(SSB_PMU1_PLLCTL2_NDIVINT | SSB_PMU1_PLLCTL2_NDIVMODE);
290 : 0 : pllctl |= ((u32)e->ndiv_int << SSB_PMU1_PLLCTL2_NDIVINT_SHIFT) & SSB_PMU1_PLLCTL2_NDIVINT;
291 : 0 : pllctl |= (1 << SSB_PMU1_PLLCTL2_NDIVMODE_SHIFT) & SSB_PMU1_PLLCTL2_NDIVMODE;
292 : 0 : ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL2, pllctl);
293 : :
294 : : /* Set ndiv frac */
295 : 0 : pllctl = ssb_chipco_pll_read(cc, SSB_PMU1_PLLCTL3);
296 : 0 : pllctl &= ~SSB_PMU1_PLLCTL3_NDIVFRAC;
297 : 0 : pllctl |= ((u32)e->ndiv_frac << SSB_PMU1_PLLCTL3_NDIVFRAC_SHIFT) & SSB_PMU1_PLLCTL3_NDIVFRAC;
298 : 0 : ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL3, pllctl);
299 : :
300 : : /* Change the drive strength, if required. */
301 [ # # ]: 0 : if (buffer_strength) {
302 : 0 : pllctl = ssb_chipco_pll_read(cc, SSB_PMU1_PLLCTL5);
303 : 0 : pllctl &= ~SSB_PMU1_PLLCTL5_CLKDRV;
304 : 0 : pllctl |= (buffer_strength << SSB_PMU1_PLLCTL5_CLKDRV_SHIFT) & SSB_PMU1_PLLCTL5_CLKDRV;
305 : 0 : ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL5, pllctl);
306 : : }
307 : :
308 : : /* Tune the crystalfreq and the divisor. */
309 : 0 : pmuctl = chipco_read32(cc, SSB_CHIPCO_PMU_CTL);
310 : 0 : pmuctl &= ~(SSB_CHIPCO_PMU_CTL_ILP_DIV | SSB_CHIPCO_PMU_CTL_XTALFREQ);
311 : 0 : pmuctl |= ((((u32)e->freq + 127) / 128 - 1) << SSB_CHIPCO_PMU_CTL_ILP_DIV_SHIFT)
312 : : & SSB_CHIPCO_PMU_CTL_ILP_DIV;
313 : 0 : pmuctl |= ((u32)e->xf << SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT) & SSB_CHIPCO_PMU_CTL_XTALFREQ;
314 : 0 : chipco_write32(cc, SSB_CHIPCO_PMU_CTL, pmuctl);
315 : : }
316 : :
317 : 0 : static void ssb_pmu_pll_init(struct ssb_chipcommon *cc)
318 : : {
319 : 0 : struct ssb_bus *bus = cc->dev->bus;
320 : 0 : u32 crystalfreq = 0; /* in kHz. 0 = keep default freq. */
321 : :
322 : 0 : if (bus->bustype == SSB_BUSTYPE_SSB) {
323 : : #ifdef CONFIG_BCM47XX
324 : : char buf[20];
325 : : if (bcm47xx_nvram_getenv("xtalfreq", buf, sizeof(buf)) >= 0)
326 : : crystalfreq = simple_strtoul(buf, NULL, 0);
327 : : #endif
328 : 0 : }
329 : :
330 [ # # # # : 0 : switch (bus->chip_id) {
# # ]
331 : 0 : case 0x4312:
332 : : case 0x4325:
333 : 0 : ssb_pmu1_pllinit_r0(cc, crystalfreq);
334 : 0 : break;
335 : 0 : case 0x4328:
336 : 0 : ssb_pmu0_pllinit_r0(cc, crystalfreq);
337 : 0 : break;
338 : : case 0x5354:
339 : 0 : if (crystalfreq == 0)
340 : 0 : crystalfreq = 25000;
341 : 0 : ssb_pmu0_pllinit_r0(cc, crystalfreq);
342 : 0 : break;
343 : 0 : case 0x4322:
344 [ # # ]: 0 : if (cc->pmu.rev == 2) {
345 : 0 : chipco_write32(cc, SSB_CHIPCO_PLLCTL_ADDR, 0x0000000A);
346 : 0 : chipco_write32(cc, SSB_CHIPCO_PLLCTL_DATA, 0x380005C0);
347 : : }
348 : : break;
349 : : case 43222:
350 : : break;
351 : 0 : default:
352 : 0 : dev_err(cc->dev->dev, "ERROR: PLL init unknown for device %04X\n",
353 : : bus->chip_id);
354 : : }
355 : 0 : }
356 : :
357 : : struct pmu_res_updown_tab_entry {
358 : : u8 resource; /* The resource number */
359 : : u16 updown; /* The updown value */
360 : : };
361 : :
362 : : enum pmu_res_depend_tab_task {
363 : : PMU_RES_DEP_SET = 1,
364 : : PMU_RES_DEP_ADD,
365 : : PMU_RES_DEP_REMOVE,
366 : : };
367 : :
368 : : struct pmu_res_depend_tab_entry {
369 : : u8 resource; /* The resource number */
370 : : u8 task; /* SET | ADD | REMOVE */
371 : : u32 depend; /* The depend mask */
372 : : };
373 : :
374 : : static const struct pmu_res_updown_tab_entry pmu_res_updown_tab_4328a0[] = {
375 : : { .resource = SSB_PMURES_4328_EXT_SWITCHER_PWM, .updown = 0x0101, },
376 : : { .resource = SSB_PMURES_4328_BB_SWITCHER_PWM, .updown = 0x1F01, },
377 : : { .resource = SSB_PMURES_4328_BB_SWITCHER_BURST, .updown = 0x010F, },
378 : : { .resource = SSB_PMURES_4328_BB_EXT_SWITCHER_BURST, .updown = 0x0101, },
379 : : { .resource = SSB_PMURES_4328_ILP_REQUEST, .updown = 0x0202, },
380 : : { .resource = SSB_PMURES_4328_RADIO_SWITCHER_PWM, .updown = 0x0F01, },
381 : : { .resource = SSB_PMURES_4328_RADIO_SWITCHER_BURST, .updown = 0x0F01, },
382 : : { .resource = SSB_PMURES_4328_ROM_SWITCH, .updown = 0x0101, },
383 : : { .resource = SSB_PMURES_4328_PA_REF_LDO, .updown = 0x0F01, },
384 : : { .resource = SSB_PMURES_4328_RADIO_LDO, .updown = 0x0F01, },
385 : : { .resource = SSB_PMURES_4328_AFE_LDO, .updown = 0x0F01, },
386 : : { .resource = SSB_PMURES_4328_PLL_LDO, .updown = 0x0F01, },
387 : : { .resource = SSB_PMURES_4328_BG_FILTBYP, .updown = 0x0101, },
388 : : { .resource = SSB_PMURES_4328_TX_FILTBYP, .updown = 0x0101, },
389 : : { .resource = SSB_PMURES_4328_RX_FILTBYP, .updown = 0x0101, },
390 : : { .resource = SSB_PMURES_4328_XTAL_PU, .updown = 0x0101, },
391 : : { .resource = SSB_PMURES_4328_XTAL_EN, .updown = 0xA001, },
392 : : { .resource = SSB_PMURES_4328_BB_PLL_FILTBYP, .updown = 0x0101, },
393 : : { .resource = SSB_PMURES_4328_RF_PLL_FILTBYP, .updown = 0x0101, },
394 : : { .resource = SSB_PMURES_4328_BB_PLL_PU, .updown = 0x0701, },
395 : : };
396 : :
397 : : static const struct pmu_res_depend_tab_entry pmu_res_depend_tab_4328a0[] = {
398 : : {
399 : : /* Adjust ILP Request to avoid forcing EXT/BB into burst mode. */
400 : : .resource = SSB_PMURES_4328_ILP_REQUEST,
401 : : .task = PMU_RES_DEP_SET,
402 : : .depend = ((1 << SSB_PMURES_4328_EXT_SWITCHER_PWM) |
403 : : (1 << SSB_PMURES_4328_BB_SWITCHER_PWM)),
404 : : },
405 : : };
406 : :
407 : : static const struct pmu_res_updown_tab_entry pmu_res_updown_tab_4325a0[] = {
408 : : { .resource = SSB_PMURES_4325_XTAL_PU, .updown = 0x1501, },
409 : : };
410 : :
411 : : static const struct pmu_res_depend_tab_entry pmu_res_depend_tab_4325a0[] = {
412 : : {
413 : : /* Adjust HT-Available dependencies. */
414 : : .resource = SSB_PMURES_4325_HT_AVAIL,
415 : : .task = PMU_RES_DEP_ADD,
416 : : .depend = ((1 << SSB_PMURES_4325_RX_PWRSW_PU) |
417 : : (1 << SSB_PMURES_4325_TX_PWRSW_PU) |
418 : : (1 << SSB_PMURES_4325_LOGEN_PWRSW_PU) |
419 : : (1 << SSB_PMURES_4325_AFE_PWRSW_PU)),
420 : : },
421 : : };
422 : :
423 : : static void ssb_pmu_resources_init(struct ssb_chipcommon *cc)
424 : : {
425 : : struct ssb_bus *bus = cc->dev->bus;
426 : : u32 min_msk = 0, max_msk = 0;
427 : : unsigned int i;
428 : : const struct pmu_res_updown_tab_entry *updown_tab = NULL;
429 : : unsigned int updown_tab_size = 0;
430 : : const struct pmu_res_depend_tab_entry *depend_tab = NULL;
431 : : unsigned int depend_tab_size = 0;
432 : :
433 : : switch (bus->chip_id) {
434 : : case 0x4312:
435 : : min_msk = 0xCBB;
436 : : break;
437 : : case 0x4322:
438 : : case 43222:
439 : : /* We keep the default settings:
440 : : * min_msk = 0xCBB
441 : : * max_msk = 0x7FFFF
442 : : */
443 : : break;
444 : : case 0x4325:
445 : : /* Power OTP down later. */
446 : : min_msk = (1 << SSB_PMURES_4325_CBUCK_BURST) |
447 : : (1 << SSB_PMURES_4325_LNLDO2_PU);
448 : : if (chipco_read32(cc, SSB_CHIPCO_CHIPSTAT) &
449 : : SSB_CHIPCO_CHST_4325_PMUTOP_2B)
450 : : min_msk |= (1 << SSB_PMURES_4325_CLDO_CBUCK_BURST);
451 : : /* The PLL may turn on, if it decides so. */
452 : : max_msk = 0xFFFFF;
453 : : updown_tab = pmu_res_updown_tab_4325a0;
454 : : updown_tab_size = ARRAY_SIZE(pmu_res_updown_tab_4325a0);
455 : : depend_tab = pmu_res_depend_tab_4325a0;
456 : : depend_tab_size = ARRAY_SIZE(pmu_res_depend_tab_4325a0);
457 : : break;
458 : : case 0x4328:
459 : : min_msk = (1 << SSB_PMURES_4328_EXT_SWITCHER_PWM) |
460 : : (1 << SSB_PMURES_4328_BB_SWITCHER_PWM) |
461 : : (1 << SSB_PMURES_4328_XTAL_EN);
462 : : /* The PLL may turn on, if it decides so. */
463 : : max_msk = 0xFFFFF;
464 : : updown_tab = pmu_res_updown_tab_4328a0;
465 : : updown_tab_size = ARRAY_SIZE(pmu_res_updown_tab_4328a0);
466 : : depend_tab = pmu_res_depend_tab_4328a0;
467 : : depend_tab_size = ARRAY_SIZE(pmu_res_depend_tab_4328a0);
468 : : break;
469 : : case 0x5354:
470 : : /* The PLL may turn on, if it decides so. */
471 : : max_msk = 0xFFFFF;
472 : : break;
473 : : default:
474 : : dev_err(cc->dev->dev, "ERROR: PMU resource config unknown for device %04X\n",
475 : : bus->chip_id);
476 : : }
477 : :
478 : : if (updown_tab) {
479 : : for (i = 0; i < updown_tab_size; i++) {
480 : : chipco_write32(cc, SSB_CHIPCO_PMU_RES_TABSEL,
481 : : updown_tab[i].resource);
482 : : chipco_write32(cc, SSB_CHIPCO_PMU_RES_UPDNTM,
483 : : updown_tab[i].updown);
484 : : }
485 : : }
486 : : if (depend_tab) {
487 : : for (i = 0; i < depend_tab_size; i++) {
488 : : chipco_write32(cc, SSB_CHIPCO_PMU_RES_TABSEL,
489 : : depend_tab[i].resource);
490 : : switch (depend_tab[i].task) {
491 : : case PMU_RES_DEP_SET:
492 : : chipco_write32(cc, SSB_CHIPCO_PMU_RES_DEPMSK,
493 : : depend_tab[i].depend);
494 : : break;
495 : : case PMU_RES_DEP_ADD:
496 : : chipco_set32(cc, SSB_CHIPCO_PMU_RES_DEPMSK,
497 : : depend_tab[i].depend);
498 : : break;
499 : : case PMU_RES_DEP_REMOVE:
500 : : chipco_mask32(cc, SSB_CHIPCO_PMU_RES_DEPMSK,
501 : : ~(depend_tab[i].depend));
502 : : break;
503 : : default:
504 : : WARN_ON(1);
505 : : }
506 : : }
507 : : }
508 : :
509 : : /* Set the resource masks. */
510 : : if (min_msk)
511 : : chipco_write32(cc, SSB_CHIPCO_PMU_MINRES_MSK, min_msk);
512 : : if (max_msk)
513 : : chipco_write32(cc, SSB_CHIPCO_PMU_MAXRES_MSK, max_msk);
514 : : }
515 : :
516 : : /* http://bcm-v4.sipsolutions.net/802.11/SSB/PmuInit */
517 : 0 : void ssb_pmu_init(struct ssb_chipcommon *cc)
518 : : {
519 : 0 : u32 pmucap;
520 : :
521 [ # # ]: 0 : if (!(cc->capabilities & SSB_CHIPCO_CAP_PMU))
522 : : return;
523 : :
524 : 0 : pmucap = chipco_read32(cc, SSB_CHIPCO_PMU_CAP);
525 : 0 : cc->pmu.rev = (pmucap & SSB_CHIPCO_PMU_CAP_REVISION);
526 : :
527 : 0 : dev_dbg(cc->dev->dev, "Found rev %u PMU (capabilities 0x%08X)\n",
528 : : cc->pmu.rev, pmucap);
529 : :
530 [ # # ]: 0 : if (cc->pmu.rev == 1)
531 : 0 : chipco_mask32(cc, SSB_CHIPCO_PMU_CTL,
532 : : ~SSB_CHIPCO_PMU_CTL_NOILPONW);
533 : : else
534 : 0 : chipco_set32(cc, SSB_CHIPCO_PMU_CTL,
535 : : SSB_CHIPCO_PMU_CTL_NOILPONW);
536 : 0 : ssb_pmu_pll_init(cc);
537 : 0 : ssb_pmu_resources_init(cc);
538 : : }
539 : :
540 : 0 : void ssb_pmu_set_ldo_voltage(struct ssb_chipcommon *cc,
541 : : enum ssb_pmu_ldo_volt_id id, u32 voltage)
542 : : {
543 : 0 : struct ssb_bus *bus = cc->dev->bus;
544 : 0 : u32 addr, shift, mask;
545 : :
546 [ # # # ]: 0 : switch (bus->chip_id) {
547 : 0 : case 0x4328:
548 : : case 0x5354:
549 [ # # ]: 0 : switch (id) {
550 : : case LDO_VOLT1:
551 : : addr = 2;
552 : : shift = 25;
553 : : mask = 0xF;
554 : : break;
555 : : case LDO_VOLT2:
556 : : addr = 3;
557 : : shift = 1;
558 : : mask = 0xF;
559 : : break;
560 : : case LDO_VOLT3:
561 : : addr = 3;
562 : : shift = 9;
563 : : mask = 0xF;
564 : : break;
565 : : case LDO_PAREF:
566 : : addr = 3;
567 : : shift = 17;
568 : : mask = 0x3F;
569 : : break;
570 : : default:
571 : 0 : WARN_ON(1);
572 : 0 : return;
573 : : }
574 : : break;
575 : 0 : case 0x4312:
576 [ # # # # ]: 0 : if (WARN_ON(id != LDO_PAREF))
577 : : return;
578 : : addr = 0;
579 : : shift = 21;
580 : : mask = 0x3F;
581 : : break;
582 : : default:
583 : : return;
584 : : }
585 : :
586 : 0 : ssb_chipco_regctl_maskset(cc, addr, ~(mask << shift),
587 : 0 : (voltage & mask) << shift);
588 : : }
589 : :
590 : 0 : void ssb_pmu_set_ldo_paref(struct ssb_chipcommon *cc, bool on)
591 : : {
592 : 0 : struct ssb_bus *bus = cc->dev->bus;
593 : 0 : int ldo;
594 : :
595 [ # # # # ]: 0 : switch (bus->chip_id) {
596 : : case 0x4312:
597 : : ldo = SSB_PMURES_4312_PA_REF_LDO;
598 : : break;
599 : 0 : case 0x4328:
600 : 0 : ldo = SSB_PMURES_4328_PA_REF_LDO;
601 : 0 : break;
602 : 0 : case 0x5354:
603 : 0 : ldo = SSB_PMURES_5354_PA_REF_LDO;
604 : 0 : break;
605 : : default:
606 : : return;
607 : : }
608 : :
609 [ # # ]: 0 : if (on)
610 : 0 : chipco_set32(cc, SSB_CHIPCO_PMU_MINRES_MSK, 1 << ldo);
611 : : else
612 : 0 : chipco_mask32(cc, SSB_CHIPCO_PMU_MINRES_MSK, ~(1 << ldo));
613 : 0 : chipco_read32(cc, SSB_CHIPCO_PMU_MINRES_MSK); //SPEC FIXME found via mmiotrace - dummy read?
614 : : }
615 : :
616 : : EXPORT_SYMBOL(ssb_pmu_set_ldo_voltage);
617 : : EXPORT_SYMBOL(ssb_pmu_set_ldo_paref);
618 : :
619 : : static u32 ssb_pmu_get_alp_clock_clk0(struct ssb_chipcommon *cc)
620 : : {
621 : : u32 crystalfreq;
622 : : const struct pmu0_plltab_entry *e = NULL;
623 : :
624 : : crystalfreq = (chipco_read32(cc, SSB_CHIPCO_PMU_CTL) &
625 : : SSB_CHIPCO_PMU_CTL_XTALFREQ) >> SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT;
626 : : e = pmu0_plltab_find_entry(crystalfreq);
627 : : BUG_ON(!e);
628 : : return e->freq * 1000;
629 : : }
630 : :
631 : 0 : u32 ssb_pmu_get_alp_clock(struct ssb_chipcommon *cc)
632 : : {
633 : 0 : struct ssb_bus *bus = cc->dev->bus;
634 : :
635 [ # # ]: 0 : switch (bus->chip_id) {
636 : 0 : case 0x5354:
637 : 0 : return ssb_pmu_get_alp_clock_clk0(cc);
638 : 0 : default:
639 : 0 : dev_err(cc->dev->dev, "ERROR: PMU alp clock unknown for device %04X\n",
640 : : bus->chip_id);
641 : 0 : return 0;
642 : : }
643 : : }
644 : :
645 : 0 : u32 ssb_pmu_get_cpu_clock(struct ssb_chipcommon *cc)
646 : : {
647 : 0 : struct ssb_bus *bus = cc->dev->bus;
648 : :
649 [ # # ]: 0 : switch (bus->chip_id) {
650 : : case 0x5354:
651 : : /* 5354 chip uses a non programmable PLL of frequency 240MHz */
652 : : return 240000000;
653 : 0 : default:
654 : 0 : dev_err(cc->dev->dev, "ERROR: PMU cpu clock unknown for device %04X\n",
655 : : bus->chip_id);
656 : 0 : return 0;
657 : : }
658 : : }
659 : :
660 : 0 : u32 ssb_pmu_get_controlclock(struct ssb_chipcommon *cc)
661 : : {
662 : 0 : struct ssb_bus *bus = cc->dev->bus;
663 : :
664 [ # # ]: 0 : switch (bus->chip_id) {
665 : : case 0x5354:
666 : : return 120000000;
667 : 0 : default:
668 : 0 : dev_err(cc->dev->dev, "ERROR: PMU controlclock unknown for device %04X\n",
669 : : bus->chip_id);
670 : 0 : return 0;
671 : : }
672 : : }
673 : :
674 : 0 : void ssb_pmu_spuravoid_pllupdate(struct ssb_chipcommon *cc, int spuravoid)
675 : : {
676 : 0 : u32 pmu_ctl = 0;
677 : :
678 [ # # # ]: 0 : switch (cc->dev->bus->chip_id) {
679 : : case 0x4322:
680 : 0 : ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL0, 0x11100070);
681 : 0 : ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL1, 0x1014140a);
682 : 0 : ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL5, 0x88888854);
683 [ # # ]: 0 : if (spuravoid == 1)
684 : 0 : ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL2, 0x05201828);
685 : : else
686 : 0 : ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL2, 0x05001828);
687 : : pmu_ctl = SSB_CHIPCO_PMU_CTL_PLL_UPD;
688 : : break;
689 : 0 : case 43222:
690 [ # # ]: 0 : if (spuravoid == 1) {
691 : 0 : ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL0, 0x11500008);
692 : 0 : ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL1, 0x0C000C06);
693 : 0 : ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL2, 0x0F600a08);
694 : 0 : ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL3, 0x00000000);
695 : 0 : ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL4, 0x2001E920);
696 : 0 : ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL5, 0x88888815);
697 : : } else {
698 : 0 : ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL0, 0x11100008);
699 : 0 : ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL1, 0x0c000c06);
700 : 0 : ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL2, 0x03000a08);
701 : 0 : ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL3, 0x00000000);
702 : 0 : ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL4, 0x200005c0);
703 : 0 : ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL5, 0x88888855);
704 : : }
705 : : pmu_ctl = SSB_CHIPCO_PMU_CTL_PLL_UPD;
706 : : break;
707 : 0 : default:
708 : 0 : dev_err(cc->dev->dev,
709 : : "Unknown spuravoidance settings for chip 0x%04X, not changing PLL\n",
710 : : cc->dev->bus->chip_id);
711 : 0 : return;
712 : : }
713 : :
714 : 0 : chipco_set32(cc, SSB_CHIPCO_PMU_CTL, pmu_ctl);
715 : : }
716 : : EXPORT_SYMBOL_GPL(ssb_pmu_spuravoid_pllupdate);
|