linux/drivers/ssb/driver_chipcommon_pmu.c
<<
>>
Prefs
   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
  18static 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
  24static 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
  31struct 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
  38static 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
  56static 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. */
  71static 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
 162struct 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
 171static 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
 191static 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. */
 206static 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
 302static 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
 327struct pmu_res_updown_tab_entry {
 328        u8 resource;    /* The resource number */
 329        u16 updown;     /* The updown value */
 330};
 331
 332enum pmu_res_depend_tab_task {
 333        PMU_RES_DEP_SET = 1,
 334        PMU_RES_DEP_ADD,
 335        PMU_RES_DEP_REMOVE,
 336};
 337
 338struct 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
 344static 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
 367static 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
 377static const struct pmu_res_updown_tab_entry pmu_res_updown_tab_4325a0[] = {
 378        { .resource = SSB_PMURES_4325_XTAL_PU,                  .updown = 0x1501, },
 379};
 380
 381static 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
 393static 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
 483void 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