2 * Sonics Silicon Backplane
3 * Broadcom ChipCommon Power Management Unit driver
5 * Copyright 2009, Michael Buesch <mb@bu3sch.de>
6 * Copyright 2007, Broadcom Corporation
8 * Licensed under the GNU/GPL. See COPYING for details.
11 #include <linux/ssb/ssb.h>
12 #include <linux/ssb/ssb_regs.h>
13 #include <linux/ssb/ssb_driver_chipcommon.h>
14 #include <linux/delay.h>
16 #include "ssb_private.h"
18 static u32
ssb_chipco_pll_read(struct ssb_chipcommon
*cc
, u32 offset
)
20 chipco_write32(cc
, SSB_CHIPCO_PLLCTL_ADDR
, offset
);
21 return chipco_read32(cc
, SSB_CHIPCO_PLLCTL_DATA
);
24 static void ssb_chipco_pll_write(struct ssb_chipcommon
*cc
,
25 u32 offset
, u32 value
)
27 chipco_write32(cc
, SSB_CHIPCO_PLLCTL_ADDR
, offset
);
28 chipco_write32(cc
, SSB_CHIPCO_PLLCTL_DATA
, value
);
31 static void ssb_chipco_regctl_maskset(struct ssb_chipcommon
*cc
,
32 u32 offset
, u32 mask
, u32 set
)
36 chipco_read32(cc
, SSB_CHIPCO_REGCTL_ADDR
);
37 chipco_write32(cc
, SSB_CHIPCO_REGCTL_ADDR
, offset
);
38 chipco_read32(cc
, SSB_CHIPCO_REGCTL_ADDR
);
39 value
= chipco_read32(cc
, SSB_CHIPCO_REGCTL_DATA
);
42 chipco_write32(cc
, SSB_CHIPCO_REGCTL_DATA
, value
);
43 chipco_read32(cc
, SSB_CHIPCO_REGCTL_DATA
);
46 struct pmu0_plltab_entry
{
47 u16 freq
; /* Crystal frequency in kHz.*/
48 u8 xf
; /* Crystal frequency value for PMU control */
53 static const struct pmu0_plltab_entry pmu0_plltab
[] = {
54 { .freq
= 12000, .xf
= 1, .wb_int
= 73, .wb_frac
= 349525, },
55 { .freq
= 13000, .xf
= 2, .wb_int
= 67, .wb_frac
= 725937, },
56 { .freq
= 14400, .xf
= 3, .wb_int
= 61, .wb_frac
= 116508, },
57 { .freq
= 15360, .xf
= 4, .wb_int
= 57, .wb_frac
= 305834, },
58 { .freq
= 16200, .xf
= 5, .wb_int
= 54, .wb_frac
= 336579, },
59 { .freq
= 16800, .xf
= 6, .wb_int
= 52, .wb_frac
= 399457, },
60 { .freq
= 19200, .xf
= 7, .wb_int
= 45, .wb_frac
= 873813, },
61 { .freq
= 19800, .xf
= 8, .wb_int
= 44, .wb_frac
= 466033, },
62 { .freq
= 20000, .xf
= 9, .wb_int
= 44, .wb_frac
= 0, },
63 { .freq
= 25000, .xf
= 10, .wb_int
= 70, .wb_frac
= 419430, },
64 { .freq
= 26000, .xf
= 11, .wb_int
= 67, .wb_frac
= 725937, },
65 { .freq
= 30000, .xf
= 12, .wb_int
= 58, .wb_frac
= 699050, },
66 { .freq
= 38400, .xf
= 13, .wb_int
= 45, .wb_frac
= 873813, },
67 { .freq
= 40000, .xf
= 14, .wb_int
= 45, .wb_frac
= 0, },
69 #define SSB_PMU0_DEFAULT_XTALFREQ 20000
71 static const struct pmu0_plltab_entry
* pmu0_plltab_find_entry(u32 crystalfreq
)
73 const struct pmu0_plltab_entry
*e
;
76 for (i
= 0; i
< ARRAY_SIZE(pmu0_plltab
); i
++) {
78 if (e
->freq
== crystalfreq
)
85 /* Tune the PLL to the crystal speed. crystalfreq is in kHz. */
86 static void ssb_pmu0_pllinit_r0(struct ssb_chipcommon
*cc
,
89 struct ssb_bus
*bus
= cc
->dev
->bus
;
90 const struct pmu0_plltab_entry
*e
= NULL
;
91 u32 pmuctl
, tmp
, pllctl
;
94 if ((bus
->chip_id
== 0x5354) && !crystalfreq
) {
95 /* The 5354 crystal freq is 25MHz */
99 e
= pmu0_plltab_find_entry(crystalfreq
);
101 e
= pmu0_plltab_find_entry(SSB_PMU0_DEFAULT_XTALFREQ
);
103 crystalfreq
= e
->freq
;
104 cc
->pmu
.crystalfreq
= e
->freq
;
106 /* Check if the PLL already is programmed to this frequency. */
107 pmuctl
= chipco_read32(cc
, SSB_CHIPCO_PMU_CTL
);
108 if (((pmuctl
& SSB_CHIPCO_PMU_CTL_XTALFREQ
) >> SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT
) == e
->xf
) {
109 /* We're already there... */
113 ssb_printk(KERN_INFO PFX
"Programming PLL to %u.%03u MHz\n",
114 (crystalfreq
/ 1000), (crystalfreq
% 1000));
116 /* First turn the PLL off. */
117 switch (bus
->chip_id
) {
119 chipco_mask32(cc
, SSB_CHIPCO_PMU_MINRES_MSK
,
120 ~(1 << SSB_PMURES_4328_BB_PLL_PU
));
121 chipco_mask32(cc
, SSB_CHIPCO_PMU_MAXRES_MSK
,
122 ~(1 << SSB_PMURES_4328_BB_PLL_PU
));
125 chipco_mask32(cc
, SSB_CHIPCO_PMU_MINRES_MSK
,
126 ~(1 << SSB_PMURES_5354_BB_PLL_PU
));
127 chipco_mask32(cc
, SSB_CHIPCO_PMU_MAXRES_MSK
,
128 ~(1 << SSB_PMURES_5354_BB_PLL_PU
));
133 for (i
= 1500; i
; i
--) {
134 tmp
= chipco_read32(cc
, SSB_CHIPCO_CLKCTLST
);
135 if (!(tmp
& SSB_CHIPCO_CLKCTLST_HAVEHT
))
139 tmp
= chipco_read32(cc
, SSB_CHIPCO_CLKCTLST
);
140 if (tmp
& SSB_CHIPCO_CLKCTLST_HAVEHT
)
141 ssb_printk(KERN_EMERG PFX
"Failed to turn the PLL off!\n");
143 /* Set PDIV in PLL control 0. */
144 pllctl
= ssb_chipco_pll_read(cc
, SSB_PMU0_PLLCTL0
);
145 if (crystalfreq
>= SSB_PMU0_PLLCTL0_PDIV_FREQ
)
146 pllctl
|= SSB_PMU0_PLLCTL0_PDIV_MSK
;
148 pllctl
&= ~SSB_PMU0_PLLCTL0_PDIV_MSK
;
149 ssb_chipco_pll_write(cc
, SSB_PMU0_PLLCTL0
, pllctl
);
151 /* Set WILD in PLL control 1. */
152 pllctl
= ssb_chipco_pll_read(cc
, SSB_PMU0_PLLCTL1
);
153 pllctl
&= ~SSB_PMU0_PLLCTL1_STOPMOD
;
154 pllctl
&= ~(SSB_PMU0_PLLCTL1_WILD_IMSK
| SSB_PMU0_PLLCTL1_WILD_FMSK
);
155 pllctl
|= ((u32
)e
->wb_int
<< SSB_PMU0_PLLCTL1_WILD_IMSK_SHIFT
) & SSB_PMU0_PLLCTL1_WILD_IMSK
;
156 pllctl
|= ((u32
)e
->wb_frac
<< SSB_PMU0_PLLCTL1_WILD_FMSK_SHIFT
) & SSB_PMU0_PLLCTL1_WILD_FMSK
;
158 pllctl
|= SSB_PMU0_PLLCTL1_STOPMOD
;
159 ssb_chipco_pll_write(cc
, SSB_PMU0_PLLCTL1
, pllctl
);
161 /* Set WILD in PLL control 2. */
162 pllctl
= ssb_chipco_pll_read(cc
, SSB_PMU0_PLLCTL2
);
163 pllctl
&= ~SSB_PMU0_PLLCTL2_WILD_IMSKHI
;
164 pllctl
|= (((u32
)e
->wb_int
>> 4) << SSB_PMU0_PLLCTL2_WILD_IMSKHI_SHIFT
) & SSB_PMU0_PLLCTL2_WILD_IMSKHI
;
165 ssb_chipco_pll_write(cc
, SSB_PMU0_PLLCTL2
, pllctl
);
167 /* Set the crystalfrequency and the divisor. */
168 pmuctl
= chipco_read32(cc
, SSB_CHIPCO_PMU_CTL
);
169 pmuctl
&= ~SSB_CHIPCO_PMU_CTL_ILP_DIV
;
170 pmuctl
|= (((crystalfreq
+ 127) / 128 - 1) << SSB_CHIPCO_PMU_CTL_ILP_DIV_SHIFT
)
171 & SSB_CHIPCO_PMU_CTL_ILP_DIV
;
172 pmuctl
&= ~SSB_CHIPCO_PMU_CTL_XTALFREQ
;
173 pmuctl
|= ((u32
)e
->xf
<< SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT
) & SSB_CHIPCO_PMU_CTL_XTALFREQ
;
174 chipco_write32(cc
, SSB_CHIPCO_PMU_CTL
, pmuctl
);
177 struct pmu1_plltab_entry
{
178 u16 freq
; /* Crystal frequency in kHz.*/
179 u8 xf
; /* Crystal frequency value for PMU control */
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, },
204 #define SSB_PMU1_DEFAULT_XTALFREQ 15360
206 static const struct pmu1_plltab_entry
* pmu1_plltab_find_entry(u32 crystalfreq
)
208 const struct pmu1_plltab_entry
*e
;
211 for (i
= 0; i
< ARRAY_SIZE(pmu1_plltab
); i
++) {
213 if (e
->freq
== crystalfreq
)
220 /* Tune the PLL to the crystal speed. crystalfreq is in kHz. */
221 static void ssb_pmu1_pllinit_r0(struct ssb_chipcommon
*cc
,
224 struct ssb_bus
*bus
= cc
->dev
->bus
;
225 const struct pmu1_plltab_entry
*e
= NULL
;
226 u32 buffer_strength
= 0;
227 u32 tmp
, pllctl
, pmuctl
;
230 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 cc
->pmu
.crystalfreq
= 20000;
238 e
= pmu1_plltab_find_entry(crystalfreq
);
240 e
= pmu1_plltab_find_entry(SSB_PMU1_DEFAULT_XTALFREQ
);
242 crystalfreq
= e
->freq
;
243 cc
->pmu
.crystalfreq
= e
->freq
;
245 /* Check if the PLL already is programmed to this frequency. */
246 pmuctl
= chipco_read32(cc
, SSB_CHIPCO_PMU_CTL
);
247 if (((pmuctl
& SSB_CHIPCO_PMU_CTL_XTALFREQ
) >> SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT
) == e
->xf
) {
248 /* We're already there... */
252 ssb_printk(KERN_INFO PFX
"Programming PLL to %u.%03u MHz\n",
253 (crystalfreq
/ 1000), (crystalfreq
% 1000));
255 /* First turn the PLL off. */
256 switch (bus
->chip_id
) {
258 chipco_mask32(cc
, SSB_CHIPCO_PMU_MINRES_MSK
,
259 ~((1 << SSB_PMURES_4325_BBPLL_PWRSW_PU
) |
260 (1 << SSB_PMURES_4325_HT_AVAIL
)));
261 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 buffer_strength
= 0x222222;
270 for (i
= 1500; i
; i
--) {
271 tmp
= chipco_read32(cc
, SSB_CHIPCO_CLKCTLST
);
272 if (!(tmp
& SSB_CHIPCO_CLKCTLST_HAVEHT
))
276 tmp
= chipco_read32(cc
, SSB_CHIPCO_CLKCTLST
);
277 if (tmp
& SSB_CHIPCO_CLKCTLST_HAVEHT
)
278 ssb_printk(KERN_EMERG PFX
"Failed to turn the PLL off!\n");
280 /* Set p1div and p2div. */
281 pllctl
= ssb_chipco_pll_read(cc
, SSB_PMU1_PLLCTL0
);
282 pllctl
&= ~(SSB_PMU1_PLLCTL0_P1DIV
| SSB_PMU1_PLLCTL0_P2DIV
);
283 pllctl
|= ((u32
)e
->p1div
<< SSB_PMU1_PLLCTL0_P1DIV_SHIFT
) & SSB_PMU1_PLLCTL0_P1DIV
;
284 pllctl
|= ((u32
)e
->p2div
<< SSB_PMU1_PLLCTL0_P2DIV_SHIFT
) & SSB_PMU1_PLLCTL0_P2DIV
;
285 ssb_chipco_pll_write(cc
, SSB_PMU1_PLLCTL0
, pllctl
);
287 /* Set ndiv int and ndiv mode */
288 pllctl
= ssb_chipco_pll_read(cc
, SSB_PMU1_PLLCTL2
);
289 pllctl
&= ~(SSB_PMU1_PLLCTL2_NDIVINT
| SSB_PMU1_PLLCTL2_NDIVMODE
);
290 pllctl
|= ((u32
)e
->ndiv_int
<< SSB_PMU1_PLLCTL2_NDIVINT_SHIFT
) & SSB_PMU1_PLLCTL2_NDIVINT
;
291 pllctl
|= (1 << SSB_PMU1_PLLCTL2_NDIVMODE_SHIFT
) & SSB_PMU1_PLLCTL2_NDIVMODE
;
292 ssb_chipco_pll_write(cc
, SSB_PMU1_PLLCTL2
, pllctl
);
295 pllctl
= ssb_chipco_pll_read(cc
, SSB_PMU1_PLLCTL3
);
296 pllctl
&= ~SSB_PMU1_PLLCTL3_NDIVFRAC
;
297 pllctl
|= ((u32
)e
->ndiv_frac
<< SSB_PMU1_PLLCTL3_NDIVFRAC_SHIFT
) & SSB_PMU1_PLLCTL3_NDIVFRAC
;
298 ssb_chipco_pll_write(cc
, SSB_PMU1_PLLCTL3
, pllctl
);
300 /* Change the drive strength, if required. */
301 if (buffer_strength
) {
302 pllctl
= ssb_chipco_pll_read(cc
, SSB_PMU1_PLLCTL5
);
303 pllctl
&= ~SSB_PMU1_PLLCTL5_CLKDRV
;
304 pllctl
|= (buffer_strength
<< SSB_PMU1_PLLCTL5_CLKDRV_SHIFT
) & SSB_PMU1_PLLCTL5_CLKDRV
;
305 ssb_chipco_pll_write(cc
, SSB_PMU1_PLLCTL5
, pllctl
);
308 /* Tune the crystalfreq and the divisor. */
309 pmuctl
= chipco_read32(cc
, SSB_CHIPCO_PMU_CTL
);
310 pmuctl
&= ~(SSB_CHIPCO_PMU_CTL_ILP_DIV
| SSB_CHIPCO_PMU_CTL_XTALFREQ
);
311 pmuctl
|= ((((u32
)e
->freq
+ 127) / 128 - 1) << SSB_CHIPCO_PMU_CTL_ILP_DIV_SHIFT
)
312 & SSB_CHIPCO_PMU_CTL_ILP_DIV
;
313 pmuctl
|= ((u32
)e
->xf
<< SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT
) & SSB_CHIPCO_PMU_CTL_XTALFREQ
;
314 chipco_write32(cc
, SSB_CHIPCO_PMU_CTL
, pmuctl
);
317 static void ssb_pmu_pll_init(struct ssb_chipcommon
*cc
)
319 struct ssb_bus
*bus
= cc
->dev
->bus
;
320 u32 crystalfreq
= 0; /* in kHz. 0 = keep default freq. */
322 if (bus
->bustype
== SSB_BUSTYPE_SSB
) {
323 /* TODO: The user may override the crystal frequency. */
326 switch (bus
->chip_id
) {
329 ssb_pmu1_pllinit_r0(cc
, crystalfreq
);
333 ssb_pmu0_pllinit_r0(cc
, crystalfreq
);
336 if (cc
->pmu
.rev
== 2) {
337 chipco_write32(cc
, SSB_CHIPCO_PLLCTL_ADDR
, 0x0000000A);
338 chipco_write32(cc
, SSB_CHIPCO_PLLCTL_DATA
, 0x380005C0);
342 ssb_printk(KERN_ERR PFX
343 "ERROR: PLL init unknown for device %04X\n",
348 struct pmu_res_updown_tab_entry
{
349 u8 resource
; /* The resource number */
350 u16 updown
; /* The updown value */
353 enum pmu_res_depend_tab_task
{
359 struct pmu_res_depend_tab_entry
{
360 u8 resource
; /* The resource number */
361 u8 task
; /* SET | ADD | REMOVE */
362 u32 depend
; /* The depend mask */
365 static const struct pmu_res_updown_tab_entry pmu_res_updown_tab_4328a0
[] = {
366 { .resource
= SSB_PMURES_4328_EXT_SWITCHER_PWM
, .updown
= 0x0101, },
367 { .resource
= SSB_PMURES_4328_BB_SWITCHER_PWM
, .updown
= 0x1F01, },
368 { .resource
= SSB_PMURES_4328_BB_SWITCHER_BURST
, .updown
= 0x010F, },
369 { .resource
= SSB_PMURES_4328_BB_EXT_SWITCHER_BURST
, .updown
= 0x0101, },
370 { .resource
= SSB_PMURES_4328_ILP_REQUEST
, .updown
= 0x0202, },
371 { .resource
= SSB_PMURES_4328_RADIO_SWITCHER_PWM
, .updown
= 0x0F01, },
372 { .resource
= SSB_PMURES_4328_RADIO_SWITCHER_BURST
, .updown
= 0x0F01, },
373 { .resource
= SSB_PMURES_4328_ROM_SWITCH
, .updown
= 0x0101, },
374 { .resource
= SSB_PMURES_4328_PA_REF_LDO
, .updown
= 0x0F01, },
375 { .resource
= SSB_PMURES_4328_RADIO_LDO
, .updown
= 0x0F01, },
376 { .resource
= SSB_PMURES_4328_AFE_LDO
, .updown
= 0x0F01, },
377 { .resource
= SSB_PMURES_4328_PLL_LDO
, .updown
= 0x0F01, },
378 { .resource
= SSB_PMURES_4328_BG_FILTBYP
, .updown
= 0x0101, },
379 { .resource
= SSB_PMURES_4328_TX_FILTBYP
, .updown
= 0x0101, },
380 { .resource
= SSB_PMURES_4328_RX_FILTBYP
, .updown
= 0x0101, },
381 { .resource
= SSB_PMURES_4328_XTAL_PU
, .updown
= 0x0101, },
382 { .resource
= SSB_PMURES_4328_XTAL_EN
, .updown
= 0xA001, },
383 { .resource
= SSB_PMURES_4328_BB_PLL_FILTBYP
, .updown
= 0x0101, },
384 { .resource
= SSB_PMURES_4328_RF_PLL_FILTBYP
, .updown
= 0x0101, },
385 { .resource
= SSB_PMURES_4328_BB_PLL_PU
, .updown
= 0x0701, },
388 static const struct pmu_res_depend_tab_entry pmu_res_depend_tab_4328a0
[] = {
390 /* Adjust ILP Request to avoid forcing EXT/BB into burst mode. */
391 .resource
= SSB_PMURES_4328_ILP_REQUEST
,
392 .task
= PMU_RES_DEP_SET
,
393 .depend
= ((1 << SSB_PMURES_4328_EXT_SWITCHER_PWM
) |
394 (1 << SSB_PMURES_4328_BB_SWITCHER_PWM
)),
398 static const struct pmu_res_updown_tab_entry pmu_res_updown_tab_4325a0
[] = {
399 { .resource
= SSB_PMURES_4325_XTAL_PU
, .updown
= 0x1501, },
402 static const struct pmu_res_depend_tab_entry pmu_res_depend_tab_4325a0
[] = {
404 /* Adjust HT-Available dependencies. */
405 .resource
= SSB_PMURES_4325_HT_AVAIL
,
406 .task
= PMU_RES_DEP_ADD
,
407 .depend
= ((1 << SSB_PMURES_4325_RX_PWRSW_PU
) |
408 (1 << SSB_PMURES_4325_TX_PWRSW_PU
) |
409 (1 << SSB_PMURES_4325_LOGEN_PWRSW_PU
) |
410 (1 << SSB_PMURES_4325_AFE_PWRSW_PU
)),
414 static void ssb_pmu_resources_init(struct ssb_chipcommon
*cc
)
416 struct ssb_bus
*bus
= cc
->dev
->bus
;
417 u32 min_msk
= 0, max_msk
= 0;
419 const struct pmu_res_updown_tab_entry
*updown_tab
= NULL
;
420 unsigned int updown_tab_size
;
421 const struct pmu_res_depend_tab_entry
*depend_tab
= NULL
;
422 unsigned int depend_tab_size
;
424 switch (bus
->chip_id
) {
427 /* We keep the default settings:
433 /* Power OTP down later. */
434 min_msk
= (1 << SSB_PMURES_4325_CBUCK_BURST
) |
435 (1 << SSB_PMURES_4325_LNLDO2_PU
);
436 if (chipco_read32(cc
, SSB_CHIPCO_CHIPSTAT
) &
437 SSB_CHIPCO_CHST_4325_PMUTOP_2B
)
438 min_msk
|= (1 << SSB_PMURES_4325_CLDO_CBUCK_BURST
);
439 /* The PLL may turn on, if it decides so. */
441 updown_tab
= pmu_res_updown_tab_4325a0
;
442 updown_tab_size
= ARRAY_SIZE(pmu_res_updown_tab_4325a0
);
443 depend_tab
= pmu_res_depend_tab_4325a0
;
444 depend_tab_size
= ARRAY_SIZE(pmu_res_depend_tab_4325a0
);
447 min_msk
= (1 << SSB_PMURES_4328_EXT_SWITCHER_PWM
) |
448 (1 << SSB_PMURES_4328_BB_SWITCHER_PWM
) |
449 (1 << SSB_PMURES_4328_XTAL_EN
);
450 /* The PLL may turn on, if it decides so. */
452 updown_tab
= pmu_res_updown_tab_4328a0
;
453 updown_tab_size
= ARRAY_SIZE(pmu_res_updown_tab_4328a0
);
454 depend_tab
= pmu_res_depend_tab_4328a0
;
455 depend_tab_size
= ARRAY_SIZE(pmu_res_depend_tab_4328a0
);
458 /* The PLL may turn on, if it decides so. */
462 ssb_printk(KERN_ERR PFX
463 "ERROR: PMU resource config unknown for device %04X\n",
468 for (i
= 0; i
< updown_tab_size
; i
++) {
469 chipco_write32(cc
, SSB_CHIPCO_PMU_RES_TABSEL
,
470 updown_tab
[i
].resource
);
471 chipco_write32(cc
, SSB_CHIPCO_PMU_RES_UPDNTM
,
472 updown_tab
[i
].updown
);
476 for (i
= 0; i
< depend_tab_size
; i
++) {
477 chipco_write32(cc
, SSB_CHIPCO_PMU_RES_TABSEL
,
478 depend_tab
[i
].resource
);
479 switch (depend_tab
[i
].task
) {
480 case PMU_RES_DEP_SET
:
481 chipco_write32(cc
, SSB_CHIPCO_PMU_RES_DEPMSK
,
482 depend_tab
[i
].depend
);
484 case PMU_RES_DEP_ADD
:
485 chipco_set32(cc
, SSB_CHIPCO_PMU_RES_DEPMSK
,
486 depend_tab
[i
].depend
);
488 case PMU_RES_DEP_REMOVE
:
489 chipco_mask32(cc
, SSB_CHIPCO_PMU_RES_DEPMSK
,
490 ~(depend_tab
[i
].depend
));
498 /* Set the resource masks. */
500 chipco_write32(cc
, SSB_CHIPCO_PMU_MINRES_MSK
, min_msk
);
502 chipco_write32(cc
, SSB_CHIPCO_PMU_MAXRES_MSK
, max_msk
);
505 void ssb_pmu_init(struct ssb_chipcommon
*cc
)
507 struct ssb_bus
*bus
= cc
->dev
->bus
;
510 if (!(cc
->capabilities
& SSB_CHIPCO_CAP_PMU
))
513 pmucap
= chipco_read32(cc
, SSB_CHIPCO_PMU_CAP
);
514 cc
->pmu
.rev
= (pmucap
& SSB_CHIPCO_PMU_CAP_REVISION
);
516 ssb_dprintk(KERN_DEBUG PFX
"Found rev %u PMU (capabilities 0x%08X)\n",
517 cc
->pmu
.rev
, pmucap
);
519 if (cc
->pmu
.rev
>= 1) {
520 if ((bus
->chip_id
== 0x4325) && (bus
->chip_rev
< 2)) {
521 chipco_mask32(cc
, SSB_CHIPCO_PMU_CTL
,
522 ~SSB_CHIPCO_PMU_CTL_NOILPONW
);
524 chipco_set32(cc
, SSB_CHIPCO_PMU_CTL
,
525 SSB_CHIPCO_PMU_CTL_NOILPONW
);
528 ssb_pmu_pll_init(cc
);
529 ssb_pmu_resources_init(cc
);
532 void ssb_pmu_set_ldo_voltage(struct ssb_chipcommon
*cc
,
533 enum ssb_pmu_ldo_volt_id id
, u32 voltage
)
535 struct ssb_bus
*bus
= cc
->dev
->bus
;
536 u32 addr
, shift
, mask
;
538 switch (bus
->chip_id
) {
568 if (SSB_WARN_ON(id
!= LDO_PAREF
))
578 ssb_chipco_regctl_maskset(cc
, addr
, ~(mask
<< shift
),
579 (voltage
& mask
) << shift
);
582 void ssb_pmu_set_ldo_paref(struct ssb_chipcommon
*cc
, bool on
)
584 struct ssb_bus
*bus
= cc
->dev
->bus
;
587 switch (bus
->chip_id
) {
589 ldo
= SSB_PMURES_4312_PA_REF_LDO
;
592 ldo
= SSB_PMURES_4328_PA_REF_LDO
;
595 ldo
= SSB_PMURES_5354_PA_REF_LDO
;
602 chipco_set32(cc
, SSB_CHIPCO_PMU_MINRES_MSK
, 1 << ldo
);
604 chipco_mask32(cc
, SSB_CHIPCO_PMU_MINRES_MSK
, ~(1 << ldo
));
605 chipco_read32(cc
, SSB_CHIPCO_PMU_MINRES_MSK
); //SPEC FIXME found via mmiotrace - dummy read?
608 EXPORT_SYMBOL(ssb_pmu_set_ldo_voltage
);
609 EXPORT_SYMBOL(ssb_pmu_set_ldo_paref
);