1 /* 2 * Sonics Silicon Backplane 3 * Broadcom ChipCommon Power Management Unit driver 4 * 5 * Copyright 2009, Michael Buesch <mb@bu3sch.de> 6 * Copyright 2007, Broadcom Corporation 7 * 8 * Licensed under the GNU/GPL. See COPYING for details. 9 */ 10 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> 15 16 #include "ssb_private.h" 17 18 static u32 ssb_chipco_pll_read(struct ssb_chipcommon *cc, u32 offset) 19 { 20 chipco_write32(cc, SSB_CHIPCO_PLLCTL_ADDR, offset); 21 return chipco_read32(cc, SSB_CHIPCO_PLLCTL_DATA); 22 } 23 24 static void ssb_chipco_pll_write(struct ssb_chipcommon *cc, 25 u32 offset, u32 value) 26 { 27 chipco_write32(cc, SSB_CHIPCO_PLLCTL_ADDR, offset); 28 chipco_write32(cc, SSB_CHIPCO_PLLCTL_DATA, value); 29 } 30 31 struct pmu0_plltab_entry { 32 u16 freq; /* Crystal frequency in kHz.*/ 33 u8 xf; /* Crystal frequency value for PMU control */ 34 u8 wb_int; 35 u32 wb_frac; 36 }; 37 38 static const struct pmu0_plltab_entry pmu0_plltab[] = { 39 { .freq = 12000, .xf = 1, .wb_int = 73, .wb_frac = 349525, }, 40 { .freq = 13000, .xf = 2, .wb_int = 67, .wb_frac = 725937, }, 41 { .freq = 14400, .xf = 3, .wb_int = 61, .wb_frac = 116508, }, 42 { .freq = 15360, .xf = 4, .wb_int = 57, .wb_frac = 305834, }, 43 { .freq = 16200, .xf = 5, .wb_int = 54, .wb_frac = 336579, }, 44 { .freq = 16800, .xf = 6, .wb_int = 52, .wb_frac = 399457, }, 45 { .freq = 19200, .xf = 7, .wb_int = 45, .wb_frac = 873813, }, 46 { .freq = 19800, .xf = 8, .wb_int = 44, .wb_frac = 466033, }, 47 { .freq = 20000, .xf = 9, .wb_int = 44, .wb_frac = 0, }, 48 { .freq = 25000, .xf = 10, .wb_int = 70, .wb_frac = 419430, }, 49 { .freq = 26000, .xf = 11, .wb_int = 67, .wb_frac = 725937, }, 50 { .freq = 30000, .xf = 12, .wb_int = 58, .wb_frac = 699050, }, 51 { .freq = 38400, .xf = 13, .wb_int = 45, .wb_frac = 873813, }, 52 { .freq = 40000, .xf = 14, .wb_int = 45, .wb_frac = 0, }, 53 }; 54 #define SSB_PMU0_DEFAULT_XTALFREQ 20000 55 56 static const struct pmu0_plltab_entry * pmu0_plltab_find_entry(u32 crystalfreq) 57 { 58 const struct pmu0_plltab_entry *e; 59 unsigned int i; 60 61 for (i = 0; i < ARRAY_SIZE(pmu0_plltab); i++) { 62 e = &pmu0_plltab[i]; 63 if (e->freq == crystalfreq) 64 return e; 65 } 66 67 return NULL; 68 } 69 70 /* Tune the PLL to the crystal speed. crystalfreq is in kHz. */ 71 static void ssb_pmu0_pllinit_r0(struct ssb_chipcommon *cc, 72 u32 crystalfreq) 73 { 74 struct ssb_bus *bus = cc->dev->bus; 75 const struct pmu0_plltab_entry *e = NULL; 76 u32 pmuctl, tmp, pllctl; 77 unsigned int i; 78 79 if ((bus->chip_id == 0x5354) && !crystalfreq) { 80 /* The 5354 crystal freq is 25MHz */ 81 crystalfreq = 25000; 82 } 83 if (crystalfreq) 84 e = pmu0_plltab_find_entry(crystalfreq); 85 if (!e) 86 e = pmu0_plltab_find_entry(SSB_PMU0_DEFAULT_XTALFREQ); 87 BUG_ON(!e); 88 crystalfreq = e->freq; 89 cc->pmu.crystalfreq = e->freq; 90 91 /* Check if the PLL already is programmed to this frequency. */ 92 pmuctl = chipco_read32(cc, SSB_CHIPCO_PMU_CTL); 93 if (((pmuctl & SSB_CHIPCO_PMU_CTL_XTALFREQ) >> SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT) == e->xf) { 94 /* We're already there... */ 95 return; 96 } 97 98 ssb_printk(KERN_INFO PFX "Programming PLL to %u.%03u MHz\n", 99 (crystalfreq / 1000), (crystalfreq % 1000)); 100 101 /* First turn the PLL off. */ 102 switch (bus->chip_id) { 103 case 0x4328: 104 chipco_mask32(cc, SSB_CHIPCO_PMU_MINRES_MSK, 105 ~(1 << SSB_PMURES_4328_BB_PLL_PU)); 106 chipco_mask32(cc, SSB_CHIPCO_PMU_MAXRES_MSK, 107 ~(1 << SSB_PMURES_4328_BB_PLL_PU)); 108 break; 109 case 0x5354: 110 chipco_mask32(cc, SSB_CHIPCO_PMU_MINRES_MSK, 111 ~(1 << SSB_PMURES_5354_BB_PLL_PU)); 112 chipco_mask32(cc, SSB_CHIPCO_PMU_MAXRES_MSK, 113 ~(1 << SSB_PMURES_5354_BB_PLL_PU)); 114 break; 115 default: 116 SSB_WARN_ON(1); 117 } 118 for (i = 1500; i; i--) { 119 tmp = chipco_read32(cc, SSB_CHIPCO_CLKCTLST); 120 if (!(tmp & SSB_CHIPCO_CLKCTLST_HAVEHT)) 121 break; 122 udelay(10); 123 } 124 tmp = chipco_read32(cc, SSB_CHIPCO_CLKCTLST); 125 if (tmp & SSB_CHIPCO_CLKCTLST_HAVEHT) 126 ssb_printk(KERN_EMERG PFX "Failed to turn the PLL off!\n"); 127 128 /* Set PDIV in PLL control 0. */ 129 pllctl = ssb_chipco_pll_read(cc, SSB_PMU0_PLLCTL0); 130 if (crystalfreq >= SSB_PMU0_PLLCTL0_PDIV_FREQ) 131 pllctl |= SSB_PMU0_PLLCTL0_PDIV_MSK; 132 else 133 pllctl &= ~SSB_PMU0_PLLCTL0_PDIV_MSK; 134 ssb_chipco_pll_write(cc, SSB_PMU0_PLLCTL0, pllctl); 135 136 /* Set WILD in PLL control 1. */ 137 pllctl = ssb_chipco_pll_read(cc, SSB_PMU0_PLLCTL1); 138 pllctl &= ~SSB_PMU0_PLLCTL1_STOPMOD; 139 pllctl &= ~(SSB_PMU0_PLLCTL1_WILD_IMSK | SSB_PMU0_PLLCTL1_WILD_FMSK); 140 pllctl |= ((u32)e->wb_int << SSB_PMU0_PLLCTL1_WILD_IMSK_SHIFT) & SSB_PMU0_PLLCTL1_WILD_IMSK; 141 pllctl |= ((u32)e->wb_frac << SSB_PMU0_PLLCTL1_WILD_FMSK_SHIFT) & SSB_PMU0_PLLCTL1_WILD_FMSK; 142 if (e->wb_frac == 0) 143 pllctl |= SSB_PMU0_PLLCTL1_STOPMOD; 144 ssb_chipco_pll_write(cc, SSB_PMU0_PLLCTL1, pllctl); 145 146 /* Set WILD in PLL control 2. */ 147 pllctl = ssb_chipco_pll_read(cc, SSB_PMU0_PLLCTL2); 148 pllctl &= ~SSB_PMU0_PLLCTL2_WILD_IMSKHI; 149 pllctl |= (((u32)e->wb_int >> 4) << SSB_PMU0_PLLCTL2_WILD_IMSKHI_SHIFT) & SSB_PMU0_PLLCTL2_WILD_IMSKHI; 150 ssb_chipco_pll_write(cc, SSB_PMU0_PLLCTL2, pllctl); 151 152 /* Set the crystalfrequency and the divisor. */ 153 pmuctl = chipco_read32(cc, SSB_CHIPCO_PMU_CTL); 154 pmuctl &= ~SSB_CHIPCO_PMU_CTL_ILP_DIV; 155 pmuctl |= (((crystalfreq + 127) / 128 - 1) << SSB_CHIPCO_PMU_CTL_ILP_DIV_SHIFT) 156 & SSB_CHIPCO_PMU_CTL_ILP_DIV; 157 pmuctl &= ~SSB_CHIPCO_PMU_CTL_XTALFREQ; 158 pmuctl |= ((u32)e->xf << SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT) & SSB_CHIPCO_PMU_CTL_XTALFREQ; 159 chipco_write32(cc, SSB_CHIPCO_PMU_CTL, pmuctl); 160 } 161 162 struct pmu1_plltab_entry { 163 u16 freq; /* Crystal frequency in kHz.*/ 164 u8 xf; /* Crystal frequency value for PMU control */ 165 u8 ndiv_int; 166 u32 ndiv_frac; 167 u8 p1div; 168 u8 p2div; 169 }; 170 171 static const struct pmu1_plltab_entry pmu1_plltab[] = { 172 { .freq = 12000, .xf = 1, .p1div = 3, .p2div = 22, .ndiv_int = 0x9, .ndiv_frac = 0xFFFFEF, }, 173 { .freq = 13000, .xf = 2, .p1div = 1, .p2div = 6, .ndiv_int = 0xb, .ndiv_frac = 0x483483, }, 174 { .freq = 14400, .xf = 3, .p1div = 1, .p2div = 10, .ndiv_int = 0xa, .ndiv_frac = 0x1C71C7, }, 175 { .freq = 15360, .xf = 4, .p1div = 1, .p2div = 5, .ndiv_int = 0xb, .ndiv_frac = 0x755555, }, 176 { .freq = 16200, .xf = 5, .p1div = 1, .p2div = 10, .ndiv_int = 0x5, .ndiv_frac = 0x6E9E06, }, 177 { .freq = 16800, .xf = 6, .p1div = 1, .p2div = 10, .ndiv_int = 0x5, .ndiv_frac = 0x3CF3CF, }, 178 { .freq = 19200, .xf = 7, .p1div = 1, .p2div = 9, .ndiv_int = 0x5, .ndiv_frac = 0x17B425, }, 179 { .freq = 19800, .xf = 8, .p1div = 1, .p2div = 11, .ndiv_int = 0x4, .ndiv_frac = 0xA57EB, }, 180 { .freq = 20000, .xf = 9, .p1div = 1, .p2div = 11, .ndiv_int = 0x4, .ndiv_frac = 0, }, 181 { .freq = 24000, .xf = 10, .p1div = 3, .p2div = 11, .ndiv_int = 0xa, .ndiv_frac = 0, }, 182 { .freq = 25000, .xf = 11, .p1div = 5, .p2div = 16, .ndiv_int = 0xb, .ndiv_frac = 0, }, 183 { .freq = 26000, .xf = 12, .p1div = 1, .p2div = 2, .ndiv_int = 0x10, .ndiv_frac = 0xEC4EC4, }, 184 { .freq = 30000, .xf = 13, .p1div = 3, .p2div = 8, .ndiv_int = 0xb, .ndiv_frac = 0, }, 185 { .freq = 38400, .xf = 14, .p1div = 1, .p2div = 5, .ndiv_int = 0x4, .ndiv_frac = 0x955555, }, 186 { .freq = 40000, .xf = 15, .p1div = 1, .p2div = 2, .ndiv_int = 0xb, .ndiv_frac = 0, }, 187 }; 188 189 #define SSB_PMU1_DEFAULT_XTALFREQ 15360 190 191 static const struct pmu1_plltab_entry * pmu1_plltab_find_entry(u32 crystalfreq) 192 { 193 const struct pmu1_plltab_entry *e; 194 unsigned int i; 195 196 for (i = 0; i < ARRAY_SIZE(pmu1_plltab); i++) { 197 e = &pmu1_plltab[i]; 198 if (e->freq == crystalfreq) 199 return e; 200 } 201 202 return NULL; 203 } 204 205 /* Tune the PLL to the crystal speed. crystalfreq is in kHz. */ 206 static void ssb_pmu1_pllinit_r0(struct ssb_chipcommon *cc, 207 u32 crystalfreq) 208 { 209 struct ssb_bus *bus = cc->dev->bus; 210 const struct pmu1_plltab_entry *e = NULL; 211 u32 buffer_strength = 0; 212 u32 tmp, pllctl, pmuctl; 213 unsigned int i; 214 215 if (bus->chip_id == 0x4312) { 216 /* We do not touch the BCM4312 PLL and assume 217 * the default crystal settings work out-of-the-box. */ 218 cc->pmu.crystalfreq = 20000; 219 return; 220 } 221 222 if (crystalfreq) 223 e = pmu1_plltab_find_entry(crystalfreq); 224 if (!e) 225 e = pmu1_plltab_find_entry(SSB_PMU1_DEFAULT_XTALFREQ); 226 BUG_ON(!e); 227 crystalfreq = e->freq; 228 cc->pmu.crystalfreq = e->freq; 229 230 /* Check if the PLL already is programmed to this frequency. */ 231 pmuctl = chipco_read32(cc, SSB_CHIPCO_PMU_CTL); 232 if (((pmuctl & SSB_CHIPCO_PMU_CTL_XTALFREQ) >> SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT) == e->xf) { 233 /* We're already there... */ 234 return; 235 } 236 237 ssb_printk(KERN_INFO PFX "Programming PLL to %u.%03u MHz\n", 238 (crystalfreq / 1000), (crystalfreq % 1000)); 239 240 /* First turn the PLL off. */ 241 switch (bus->chip_id) { 242 case 0x4325: 243 chipco_mask32(cc, SSB_CHIPCO_PMU_MINRES_MSK, 244 ~((1 << SSB_PMURES_4325_BBPLL_PWRSW_PU) | 245 (1 << SSB_PMURES_4325_HT_AVAIL))); 246 chipco_mask32(cc, SSB_CHIPCO_PMU_MAXRES_MSK, 247 ~((1 << SSB_PMURES_4325_BBPLL_PWRSW_PU) | 248 (1 << SSB_PMURES_4325_HT_AVAIL))); 249 /* Adjust the BBPLL to 2 on all channels later. */ 250 buffer_strength = 0x222222; 251 break; 252 default: 253 SSB_WARN_ON(1); 254 } 255 for (i = 1500; i; i--) { 256 tmp = chipco_read32(cc, SSB_CHIPCO_CLKCTLST); 257 if (!(tmp & SSB_CHIPCO_CLKCTLST_HAVEHT)) 258 break; 259 udelay(10); 260 } 261 tmp = chipco_read32(cc, SSB_CHIPCO_CLKCTLST); 262 if (tmp & SSB_CHIPCO_CLKCTLST_HAVEHT) 263 ssb_printk(KERN_EMERG PFX "Failed to turn the PLL off!\n"); 264 265 /* Set p1div and p2div. */ 266 pllctl = ssb_chipco_pll_read(cc, SSB_PMU1_PLLCTL0); 267 pllctl &= ~(SSB_PMU1_PLLCTL0_P1DIV | SSB_PMU1_PLLCTL0_P2DIV); 268 pllctl |= ((u32)e->p1div << SSB_PMU1_PLLCTL0_P1DIV_SHIFT) & SSB_PMU1_PLLCTL0_P1DIV; 269 pllctl |= ((u32)e->p2div << SSB_PMU1_PLLCTL0_P2DIV_SHIFT) & SSB_PMU1_PLLCTL0_P2DIV; 270 ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL0, pllctl); 271 272 /* Set ndiv int and ndiv mode */ 273 pllctl = ssb_chipco_pll_read(cc, SSB_PMU1_PLLCTL2); 274 pllctl &= ~(SSB_PMU1_PLLCTL2_NDIVINT | SSB_PMU1_PLLCTL2_NDIVMODE); 275 pllctl |= ((u32)e->ndiv_int << SSB_PMU1_PLLCTL2_NDIVINT_SHIFT) & SSB_PMU1_PLLCTL2_NDIVINT; 276 pllctl |= (1 << SSB_PMU1_PLLCTL2_NDIVMODE_SHIFT) & SSB_PMU1_PLLCTL2_NDIVMODE; 277 ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL2, pllctl); 278 279 /* Set ndiv frac */ 280 pllctl = ssb_chipco_pll_read(cc, SSB_PMU1_PLLCTL3); 281 pllctl &= ~SSB_PMU1_PLLCTL3_NDIVFRAC; 282 pllctl |= ((u32)e->ndiv_frac << SSB_PMU1_PLLCTL3_NDIVFRAC_SHIFT) & SSB_PMU1_PLLCTL3_NDIVFRAC; 283 ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL3, pllctl); 284 285 /* Change the drive strength, if required. */ 286 if (buffer_strength) { 287 pllctl = ssb_chipco_pll_read(cc, SSB_PMU1_PLLCTL5); 288 pllctl &= ~SSB_PMU1_PLLCTL5_CLKDRV; 289 pllctl |= (buffer_strength << SSB_PMU1_PLLCTL5_CLKDRV_SHIFT) & SSB_PMU1_PLLCTL5_CLKDRV; 290 ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL5, pllctl); 291 } 292 293 /* Tune the crystalfreq and the divisor. */ 294 pmuctl = chipco_read32(cc, SSB_CHIPCO_PMU_CTL); 295 pmuctl &= ~(SSB_CHIPCO_PMU_CTL_ILP_DIV | SSB_CHIPCO_PMU_CTL_XTALFREQ); 296 pmuctl |= ((((u32)e->freq + 127) / 128 - 1) << SSB_CHIPCO_PMU_CTL_ILP_DIV_SHIFT) 297 & SSB_CHIPCO_PMU_CTL_ILP_DIV; 298 pmuctl |= ((u32)e->xf << SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT) & SSB_CHIPCO_PMU_CTL_XTALFREQ; 299 chipco_write32(cc, SSB_CHIPCO_PMU_CTL, pmuctl); 300 } 301 302 static void ssb_pmu_pll_init(struct ssb_chipcommon *cc) 303 { 304 struct ssb_bus *bus = cc->dev->bus; 305 u32 crystalfreq = 0; /* in kHz. 0 = keep default freq. */ 306 307 if (bus->bustype == SSB_BUSTYPE_SSB) { 308 /* TODO: The user may override the crystal frequency. */ 309 } 310 311 switch (bus->chip_id) { 312 case 0x4312: 313 case 0x4325: 314 ssb_pmu1_pllinit_r0(cc, crystalfreq); 315 break; 316 case 0x4328: 317 case 0x5354: 318 ssb_pmu0_pllinit_r0(cc, crystalfreq); 319 break; 320 default: 321 ssb_printk(KERN_ERR PFX 322 "ERROR: PLL init unknown for device %04X\n", 323 bus->chip_id); 324 } 325 } 326 327 struct pmu_res_updown_tab_entry { 328 u8 resource; /* The resource number */ 329 u16 updown; /* The updown value */ 330 }; 331 332 enum pmu_res_depend_tab_task { 333 PMU_RES_DEP_SET = 1, 334 PMU_RES_DEP_ADD, 335 PMU_RES_DEP_REMOVE, 336 }; 337 338 struct pmu_res_depend_tab_entry { 339 u8 resource; /* The resource number */ 340 u8 task; /* SET | ADD | REMOVE */ 341 u32 depend; /* The depend mask */ 342 }; 343 344 static const struct pmu_res_updown_tab_entry pmu_res_updown_tab_4328a0[] = { 345 { .resource = SSB_PMURES_4328_EXT_SWITCHER_PWM, .updown = 0x0101, }, 346 { .resource = SSB_PMURES_4328_BB_SWITCHER_PWM, .updown = 0x1F01, }, 347 { .resource = SSB_PMURES_4328_BB_SWITCHER_BURST, .updown = 0x010F, }, 348 { .resource = SSB_PMURES_4328_BB_EXT_SWITCHER_BURST, .updown = 0x0101, }, 349 { .resource = SSB_PMURES_4328_ILP_REQUEST, .updown = 0x0202, }, 350 { .resource = SSB_PMURES_4328_RADIO_SWITCHER_PWM, .updown = 0x0F01, }, 351 { .resource = SSB_PMURES_4328_RADIO_SWITCHER_BURST, .updown = 0x0F01, }, 352 { .resource = SSB_PMURES_4328_ROM_SWITCH, .updown = 0x0101, }, 353 { .resource = SSB_PMURES_4328_PA_REF_LDO, .updown = 0x0F01, }, 354 { .resource = SSB_PMURES_4328_RADIO_LDO, .updown = 0x0F01, }, 355 { .resource = SSB_PMURES_4328_AFE_LDO, .updown = 0x0F01, }, 356 { .resource = SSB_PMURES_4328_PLL_LDO, .updown = 0x0F01, }, 357 { .resource = SSB_PMURES_4328_BG_FILTBYP, .updown = 0x0101, }, 358 { .resource = SSB_PMURES_4328_TX_FILTBYP, .updown = 0x0101, }, 359 { .resource = SSB_PMURES_4328_RX_FILTBYP, .updown = 0x0101, }, 360 { .resource = SSB_PMURES_4328_XTAL_PU, .updown = 0x0101, }, 361 { .resource = SSB_PMURES_4328_XTAL_EN, .updown = 0xA001, }, 362 { .resource = SSB_PMURES_4328_BB_PLL_FILTBYP, .updown = 0x0101, }, 363 { .resource = SSB_PMURES_4328_RF_PLL_FILTBYP, .updown = 0x0101, }, 364 { .resource = SSB_PMURES_4328_BB_PLL_PU, .updown = 0x0701, }, 365 }; 366 367 static const struct pmu_res_depend_tab_entry pmu_res_depend_tab_4328a0[] = { 368 { 369 /* Adjust ILP Request to avoid forcing EXT/BB into burst mode. */ 370 .resource = SSB_PMURES_4328_ILP_REQUEST, 371 .task = PMU_RES_DEP_SET, 372 .depend = ((1 << SSB_PMURES_4328_EXT_SWITCHER_PWM) | 373 (1 << SSB_PMURES_4328_BB_SWITCHER_PWM)), 374 }, 375 }; 376 377 static const struct pmu_res_updown_tab_entry pmu_res_updown_tab_4325a0[] = { 378 { .resource = SSB_PMURES_4325_XTAL_PU, .updown = 0x1501, }, 379 }; 380 381 static const struct pmu_res_depend_tab_entry pmu_res_depend_tab_4325a0[] = { 382 { 383 /* Adjust HT-Available dependencies. */ 384 .resource = SSB_PMURES_4325_HT_AVAIL, 385 .task = PMU_RES_DEP_ADD, 386 .depend = ((1 << SSB_PMURES_4325_RX_PWRSW_PU) | 387 (1 << SSB_PMURES_4325_TX_PWRSW_PU) | 388 (1 << SSB_PMURES_4325_LOGEN_PWRSW_PU) | 389 (1 << SSB_PMURES_4325_AFE_PWRSW_PU)), 390 }, 391 }; 392 393 static void ssb_pmu_resources_init(struct ssb_chipcommon *cc) 394 { 395 struct ssb_bus *bus = cc->dev->bus; 396 u32 min_msk = 0, max_msk = 0; 397 unsigned int i; 398 const struct pmu_res_updown_tab_entry *updown_tab = NULL; 399 unsigned int updown_tab_size; 400 const struct pmu_res_depend_tab_entry *depend_tab = NULL; 401 unsigned int depend_tab_size; 402 403 switch (bus->chip_id) { 404 case 0x4312: 405 /* We keep the default settings: 406 * min_msk = 0xCBB 407 * max_msk = 0x7FFFF 408 */ 409 break; 410 case 0x4325: 411 /* Power OTP down later. */ 412 min_msk = (1 << SSB_PMURES_4325_CBUCK_BURST) | 413 (1 << SSB_PMURES_4325_LNLDO2_PU); 414 if (chipco_read32(cc, SSB_CHIPCO_CHIPSTAT) & 415 SSB_CHIPCO_CHST_4325_PMUTOP_2B) 416 min_msk |= (1 << SSB_PMURES_4325_CLDO_CBUCK_BURST); 417 /* The PLL may turn on, if it decides so. */ 418 max_msk = 0xFFFFF; 419 updown_tab = pmu_res_updown_tab_4325a0; 420 updown_tab_size = ARRAY_SIZE(pmu_res_updown_tab_4325a0); 421 depend_tab = pmu_res_depend_tab_4325a0; 422 depend_tab_size = ARRAY_SIZE(pmu_res_depend_tab_4325a0); 423 break; 424 case 0x4328: 425 min_msk = (1 << SSB_PMURES_4328_EXT_SWITCHER_PWM) | 426 (1 << SSB_PMURES_4328_BB_SWITCHER_PWM) | 427 (1 << SSB_PMURES_4328_XTAL_EN); 428 /* The PLL may turn on, if it decides so. */ 429 max_msk = 0xFFFFF; 430 updown_tab = pmu_res_updown_tab_4328a0; 431 updown_tab_size = ARRAY_SIZE(pmu_res_updown_tab_4328a0); 432 depend_tab = pmu_res_depend_tab_4328a0; 433 depend_tab_size = ARRAY_SIZE(pmu_res_depend_tab_4328a0); 434 break; 435 case 0x5354: 436 /* The PLL may turn on, if it decides so. */ 437 max_msk = 0xFFFFF; 438 break; 439 default: 440 ssb_printk(KERN_ERR PFX 441 "ERROR: PMU resource config unknown for device %04X\n", 442 bus->chip_id); 443 } 444 445 if (updown_tab) { 446 for (i = 0; i < updown_tab_size; i++) { 447 chipco_write32(cc, SSB_CHIPCO_PMU_RES_TABSEL, 448 updown_tab[i].resource); 449 chipco_write32(cc, SSB_CHIPCO_PMU_RES_UPDNTM, 450 updown_tab[i].updown); 451 } 452 } 453 if (depend_tab) { 454 for (i = 0; i < depend_tab_size; i++) { 455 chipco_write32(cc, SSB_CHIPCO_PMU_RES_TABSEL, 456 depend_tab[i].resource); 457 switch (depend_tab[i].task) { 458 case PMU_RES_DEP_SET: 459 chipco_write32(cc, SSB_CHIPCO_PMU_RES_DEPMSK, 460 depend_tab[i].depend); 461 break; 462 case PMU_RES_DEP_ADD: 463 chipco_set32(cc, SSB_CHIPCO_PMU_RES_DEPMSK, 464 depend_tab[i].depend); 465 break; 466 case PMU_RES_DEP_REMOVE: 467 chipco_mask32(cc, SSB_CHIPCO_PMU_RES_DEPMSK, 468 ~(depend_tab[i].depend)); 469 break; 470 default: 471 SSB_WARN_ON(1); 472 } 473 } 474 } 475 476 /* Set the resource masks. */ 477 if (min_msk) 478 chipco_write32(cc, SSB_CHIPCO_PMU_MINRES_MSK, min_msk); 479 if (max_msk) 480 chipco_write32(cc, SSB_CHIPCO_PMU_MAXRES_MSK, max_msk); 481 } 482 483 void ssb_pmu_init(struct ssb_chipcommon *cc) 484 { 485 struct ssb_bus *bus = cc->dev->bus; 486 u32 pmucap; 487 488 if (!(cc->capabilities & SSB_CHIPCO_CAP_PMU)) 489 return; 490 491 pmucap = chipco_read32(cc, SSB_CHIPCO_PMU_CAP); 492 cc->pmu.rev = (pmucap & SSB_CHIPCO_PMU_CAP_REVISION); 493 494 ssb_dprintk(KERN_DEBUG PFX "Found rev %u PMU (capabilities 0x%08X)\n", 495 cc->pmu.rev, pmucap); 496 497 if (cc->pmu.rev >= 1) { 498 if ((bus->chip_id == 0x4325) && (bus->chip_rev < 2)) { 499 chipco_mask32(cc, SSB_CHIPCO_PMU_CTL, 500 ~SSB_CHIPCO_PMU_CTL_NOILPONW); 501 } else { 502 chipco_set32(cc, SSB_CHIPCO_PMU_CTL, 503 SSB_CHIPCO_PMU_CTL_NOILPONW); 504 } 505 } 506 ssb_pmu_pll_init(cc); 507 ssb_pmu_resources_init(cc); 508 } 509