linux/drivers/mfd/twl4030-power.c
<<
>>
Prefs
   1/*
   2 * linux/drivers/i2c/chips/twl4030-power.c
   3 *
   4 * Handle TWL4030 Power initialization
   5 *
   6 * Copyright (C) 2008 Nokia Corporation
   7 * Copyright (C) 2006 Texas Instruments, Inc
   8 *
   9 * Written by   Kalle Jokiniemi
  10 *              Peter De Schrijver <peter.de-schrijver@nokia.com>
  11 * Several fixes by Amit Kucheria <amit.kucheria@verdurent.com>
  12 *
  13 * This file is subject to the terms and conditions of the GNU General
  14 * Public License. See the file "COPYING" in the main directory of this
  15 * archive for more details.
  16 *
  17 * This program is distributed in the hope that it will be useful,
  18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20 * GNU General Public License for more details.
  21 *
  22 * You should have received a copy of the GNU General Public License
  23 * along with this program; if not, write to the Free Software
  24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  25 */
  26
  27#include <linux/module.h>
  28#include <linux/pm.h>
  29#include <linux/i2c/twl.h>
  30#include <linux/platform_device.h>
  31
  32#include <asm/mach-types.h>
  33
  34static u8 twl4030_start_script_address = 0x2b;
  35
  36#define PWR_P1_SW_EVENTS        0x10
  37#define PWR_DEVOFF              (1 << 0)
  38#define SEQ_OFFSYNC             (1 << 0)
  39
  40#define PHY_TO_OFF_PM_MASTER(p)         (p - 0x36)
  41#define PHY_TO_OFF_PM_RECEIVER(p)       (p - 0x5b)
  42
  43/* resource - hfclk */
  44#define R_HFCLKOUT_DEV_GRP      PHY_TO_OFF_PM_RECEIVER(0xe6)
  45
  46/* PM events */
  47#define R_P1_SW_EVENTS          PHY_TO_OFF_PM_MASTER(0x46)
  48#define R_P2_SW_EVENTS          PHY_TO_OFF_PM_MASTER(0x47)
  49#define R_P3_SW_EVENTS          PHY_TO_OFF_PM_MASTER(0x48)
  50#define R_CFG_P1_TRANSITION     PHY_TO_OFF_PM_MASTER(0x36)
  51#define R_CFG_P2_TRANSITION     PHY_TO_OFF_PM_MASTER(0x37)
  52#define R_CFG_P3_TRANSITION     PHY_TO_OFF_PM_MASTER(0x38)
  53
  54#define LVL_WAKEUP      0x08
  55
  56#define ENABLE_WARMRESET (1<<4)
  57
  58#define END_OF_SCRIPT           0x3f
  59
  60#define R_SEQ_ADD_A2S           PHY_TO_OFF_PM_MASTER(0x55)
  61#define R_SEQ_ADD_S2A12         PHY_TO_OFF_PM_MASTER(0x56)
  62#define R_SEQ_ADD_S2A3          PHY_TO_OFF_PM_MASTER(0x57)
  63#define R_SEQ_ADD_WARM          PHY_TO_OFF_PM_MASTER(0x58)
  64#define R_MEMORY_ADDRESS        PHY_TO_OFF_PM_MASTER(0x59)
  65#define R_MEMORY_DATA           PHY_TO_OFF_PM_MASTER(0x5a)
  66
  67/* resource configuration registers
  68   <RESOURCE>_DEV_GRP   at address 'n+0'
  69   <RESOURCE>_TYPE      at address 'n+1'
  70   <RESOURCE>_REMAP     at address 'n+2'
  71   <RESOURCE>_DEDICATED at address 'n+3'
  72*/
  73#define DEV_GRP_OFFSET          0
  74#define TYPE_OFFSET             1
  75#define REMAP_OFFSET            2
  76#define DEDICATED_OFFSET        3
  77
  78/* Bit positions in the registers */
  79
  80/* <RESOURCE>_DEV_GRP */
  81#define DEV_GRP_SHIFT           5
  82#define DEV_GRP_MASK            (7 << DEV_GRP_SHIFT)
  83
  84/* <RESOURCE>_TYPE */
  85#define TYPE_SHIFT              0
  86#define TYPE_MASK               (7 << TYPE_SHIFT)
  87#define TYPE2_SHIFT             3
  88#define TYPE2_MASK              (3 << TYPE2_SHIFT)
  89
  90/* <RESOURCE>_REMAP */
  91#define SLEEP_STATE_SHIFT       0
  92#define SLEEP_STATE_MASK        (0xf << SLEEP_STATE_SHIFT)
  93#define OFF_STATE_SHIFT         4
  94#define OFF_STATE_MASK          (0xf << OFF_STATE_SHIFT)
  95
  96static u8 res_config_addrs[] = {
  97        [RES_VAUX1]     = 0x17,
  98        [RES_VAUX2]     = 0x1b,
  99        [RES_VAUX3]     = 0x1f,
 100        [RES_VAUX4]     = 0x23,
 101        [RES_VMMC1]     = 0x27,
 102        [RES_VMMC2]     = 0x2b,
 103        [RES_VPLL1]     = 0x2f,
 104        [RES_VPLL2]     = 0x33,
 105        [RES_VSIM]      = 0x37,
 106        [RES_VDAC]      = 0x3b,
 107        [RES_VINTANA1]  = 0x3f,
 108        [RES_VINTANA2]  = 0x43,
 109        [RES_VINTDIG]   = 0x47,
 110        [RES_VIO]       = 0x4b,
 111        [RES_VDD1]      = 0x55,
 112        [RES_VDD2]      = 0x63,
 113        [RES_VUSB_1V5]  = 0x71,
 114        [RES_VUSB_1V8]  = 0x74,
 115        [RES_VUSB_3V1]  = 0x77,
 116        [RES_VUSBCP]    = 0x7a,
 117        [RES_REGEN]     = 0x7f,
 118        [RES_NRES_PWRON] = 0x82,
 119        [RES_CLKEN]     = 0x85,
 120        [RES_SYSEN]     = 0x88,
 121        [RES_HFCLKOUT]  = 0x8b,
 122        [RES_32KCLKOUT] = 0x8e,
 123        [RES_RESET]     = 0x91,
 124        [RES_MAIN_REF]  = 0x94,
 125};
 126
 127static int __devinit twl4030_write_script_byte(u8 address, u8 byte)
 128{
 129        int err;
 130
 131        err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
 132                                R_MEMORY_ADDRESS);
 133        if (err)
 134                goto out;
 135        err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, byte,
 136                                R_MEMORY_DATA);
 137out:
 138        return err;
 139}
 140
 141static int __devinit twl4030_write_script_ins(u8 address, u16 pmb_message,
 142                                           u8 delay, u8 next)
 143{
 144        int err;
 145
 146        address *= 4;
 147        err = twl4030_write_script_byte(address++, pmb_message >> 8);
 148        if (err)
 149                goto out;
 150        err = twl4030_write_script_byte(address++, pmb_message & 0xff);
 151        if (err)
 152                goto out;
 153        err = twl4030_write_script_byte(address++, delay);
 154        if (err)
 155                goto out;
 156        err = twl4030_write_script_byte(address++, next);
 157out:
 158        return err;
 159}
 160
 161static int __devinit twl4030_write_script(u8 address, struct twl4030_ins *script,
 162                                       int len)
 163{
 164        int err;
 165
 166        for (; len; len--, address++, script++) {
 167                if (len == 1) {
 168                        err = twl4030_write_script_ins(address,
 169                                                script->pmb_message,
 170                                                script->delay,
 171                                                END_OF_SCRIPT);
 172                        if (err)
 173                                break;
 174                } else {
 175                        err = twl4030_write_script_ins(address,
 176                                                script->pmb_message,
 177                                                script->delay,
 178                                                address + 1);
 179                        if (err)
 180                                break;
 181                }
 182        }
 183        return err;
 184}
 185
 186static int __devinit twl4030_config_wakeup3_sequence(u8 address)
 187{
 188        int err;
 189        u8 data;
 190
 191        /* Set SLEEP to ACTIVE SEQ address for P3 */
 192        err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
 193                                R_SEQ_ADD_S2A3);
 194        if (err)
 195                goto out;
 196
 197        /* P3 LVL_WAKEUP should be on LEVEL */
 198        err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
 199                                R_P3_SW_EVENTS);
 200        if (err)
 201                goto out;
 202        data |= LVL_WAKEUP;
 203        err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data,
 204                                R_P3_SW_EVENTS);
 205out:
 206        if (err)
 207                pr_err("TWL4030 wakeup sequence for P3 config error\n");
 208        return err;
 209}
 210
 211static int __devinit twl4030_config_wakeup12_sequence(u8 address)
 212{
 213        int err = 0;
 214        u8 data;
 215
 216        /* Set SLEEP to ACTIVE SEQ address for P1 and P2 */
 217        err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
 218                                R_SEQ_ADD_S2A12);
 219        if (err)
 220                goto out;
 221
 222        /* P1/P2 LVL_WAKEUP should be on LEVEL */
 223        err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
 224                                R_P1_SW_EVENTS);
 225        if (err)
 226                goto out;
 227
 228        data |= LVL_WAKEUP;
 229        err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data,
 230                                R_P1_SW_EVENTS);
 231        if (err)
 232                goto out;
 233
 234        err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
 235                                R_P2_SW_EVENTS);
 236        if (err)
 237                goto out;
 238
 239        data |= LVL_WAKEUP;
 240        err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data,
 241                                R_P2_SW_EVENTS);
 242        if (err)
 243                goto out;
 244
 245        if (machine_is_omap_3430sdp() || machine_is_omap_ldp()) {
 246                /* Disabling AC charger effect on sleep-active transitions */
 247                err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
 248                                        R_CFG_P1_TRANSITION);
 249                if (err)
 250                        goto out;
 251                data &= ~(1<<1);
 252                err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data ,
 253                                        R_CFG_P1_TRANSITION);
 254                if (err)
 255                        goto out;
 256        }
 257
 258out:
 259        if (err)
 260                pr_err("TWL4030 wakeup sequence for P1 and P2" \
 261                        "config error\n");
 262        return err;
 263}
 264
 265static int __devinit twl4030_config_sleep_sequence(u8 address)
 266{
 267        int err;
 268
 269        /* Set ACTIVE to SLEEP SEQ address in T2 memory*/
 270        err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
 271                                R_SEQ_ADD_A2S);
 272
 273        if (err)
 274                pr_err("TWL4030 sleep sequence config error\n");
 275
 276        return err;
 277}
 278
 279static int __devinit twl4030_config_warmreset_sequence(u8 address)
 280{
 281        int err;
 282        u8 rd_data;
 283
 284        /* Set WARM RESET SEQ address for P1 */
 285        err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
 286                                R_SEQ_ADD_WARM);
 287        if (err)
 288                goto out;
 289
 290        /* P1/P2/P3 enable WARMRESET */
 291        err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
 292                                R_P1_SW_EVENTS);
 293        if (err)
 294                goto out;
 295
 296        rd_data |= ENABLE_WARMRESET;
 297        err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
 298                                R_P1_SW_EVENTS);
 299        if (err)
 300                goto out;
 301
 302        err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
 303                                R_P2_SW_EVENTS);
 304        if (err)
 305                goto out;
 306
 307        rd_data |= ENABLE_WARMRESET;
 308        err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
 309                                R_P2_SW_EVENTS);
 310        if (err)
 311                goto out;
 312
 313        err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
 314                                R_P3_SW_EVENTS);
 315        if (err)
 316                goto out;
 317
 318        rd_data |= ENABLE_WARMRESET;
 319        err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
 320                                R_P3_SW_EVENTS);
 321out:
 322        if (err)
 323                pr_err("TWL4030 warmreset seq config error\n");
 324        return err;
 325}
 326
 327static int __devinit twl4030_configure_resource(struct twl4030_resconfig *rconfig)
 328{
 329        int rconfig_addr;
 330        int err;
 331        u8 type;
 332        u8 grp;
 333        u8 remap;
 334
 335        if (rconfig->resource > TOTAL_RESOURCES) {
 336                pr_err("TWL4030 Resource %d does not exist\n",
 337                        rconfig->resource);
 338                return -EINVAL;
 339        }
 340
 341        rconfig_addr = res_config_addrs[rconfig->resource];
 342
 343        /* Set resource group */
 344        err = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &grp,
 345                              rconfig_addr + DEV_GRP_OFFSET);
 346        if (err) {
 347                pr_err("TWL4030 Resource %d group could not be read\n",
 348                        rconfig->resource);
 349                return err;
 350        }
 351
 352        if (rconfig->devgroup != TWL4030_RESCONFIG_UNDEF) {
 353                grp &= ~DEV_GRP_MASK;
 354                grp |= rconfig->devgroup << DEV_GRP_SHIFT;
 355                err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
 356                                       grp, rconfig_addr + DEV_GRP_OFFSET);
 357                if (err < 0) {
 358                        pr_err("TWL4030 failed to program devgroup\n");
 359                        return err;
 360                }
 361        }
 362
 363        /* Set resource types */
 364        err = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &type,
 365                                rconfig_addr + TYPE_OFFSET);
 366        if (err < 0) {
 367                pr_err("TWL4030 Resource %d type could not be read\n",
 368                        rconfig->resource);
 369                return err;
 370        }
 371
 372        if (rconfig->type != TWL4030_RESCONFIG_UNDEF) {
 373                type &= ~TYPE_MASK;
 374                type |= rconfig->type << TYPE_SHIFT;
 375        }
 376
 377        if (rconfig->type2 != TWL4030_RESCONFIG_UNDEF) {
 378                type &= ~TYPE2_MASK;
 379                type |= rconfig->type2 << TYPE2_SHIFT;
 380        }
 381
 382        err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
 383                                type, rconfig_addr + TYPE_OFFSET);
 384        if (err < 0) {
 385                pr_err("TWL4030 failed to program resource type\n");
 386                return err;
 387        }
 388
 389        /* Set remap states */
 390        err = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &remap,
 391                              rconfig_addr + REMAP_OFFSET);
 392        if (err < 0) {
 393                pr_err("TWL4030 Resource %d remap could not be read\n",
 394                        rconfig->resource);
 395                return err;
 396        }
 397
 398        if (rconfig->remap_off != TWL4030_RESCONFIG_UNDEF) {
 399                remap &= ~OFF_STATE_MASK;
 400                remap |= rconfig->remap_off << OFF_STATE_SHIFT;
 401        }
 402
 403        if (rconfig->remap_sleep != TWL4030_RESCONFIG_UNDEF) {
 404                remap &= ~SLEEP_STATE_MASK;
 405                remap |= rconfig->remap_sleep << SLEEP_STATE_SHIFT;
 406        }
 407
 408        err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
 409                               remap,
 410                               rconfig_addr + REMAP_OFFSET);
 411        if (err < 0) {
 412                pr_err("TWL4030 failed to program remap\n");
 413                return err;
 414        }
 415
 416        return 0;
 417}
 418
 419static int __devinit load_twl4030_script(struct twl4030_script *tscript,
 420               u8 address)
 421{
 422        int err;
 423        static int order;
 424
 425        /* Make sure the script isn't going beyond last valid address (0x3f) */
 426        if ((address + tscript->size) > END_OF_SCRIPT) {
 427                pr_err("TWL4030 scripts too big error\n");
 428                return -EINVAL;
 429        }
 430
 431        err = twl4030_write_script(address, tscript->script, tscript->size);
 432        if (err)
 433                goto out;
 434
 435        if (tscript->flags & TWL4030_WRST_SCRIPT) {
 436                err = twl4030_config_warmreset_sequence(address);
 437                if (err)
 438                        goto out;
 439        }
 440        if (tscript->flags & TWL4030_WAKEUP12_SCRIPT) {
 441                err = twl4030_config_wakeup12_sequence(address);
 442                if (err)
 443                        goto out;
 444                order = 1;
 445        }
 446        if (tscript->flags & TWL4030_WAKEUP3_SCRIPT) {
 447                err = twl4030_config_wakeup3_sequence(address);
 448                if (err)
 449                        goto out;
 450        }
 451        if (tscript->flags & TWL4030_SLEEP_SCRIPT) {
 452                if (!order)
 453                        pr_warning("TWL4030: Bad order of scripts (sleep "\
 454                                        "script before wakeup) Leads to boot"\
 455                                        "failure on some boards\n");
 456                err = twl4030_config_sleep_sequence(address);
 457        }
 458out:
 459        return err;
 460}
 461
 462int twl4030_remove_script(u8 flags)
 463{
 464        int err = 0;
 465
 466        err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
 467                        TWL4030_PM_MASTER_KEY_CFG1,
 468                        TWL4030_PM_MASTER_PROTECT_KEY);
 469        if (err) {
 470                pr_err("twl4030: unable to unlock PROTECT_KEY\n");
 471                return err;
 472        }
 473
 474        err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
 475                        TWL4030_PM_MASTER_KEY_CFG2,
 476                        TWL4030_PM_MASTER_PROTECT_KEY);
 477        if (err) {
 478                pr_err("twl4030: unable to unlock PROTECT_KEY\n");
 479                return err;
 480        }
 481
 482        if (flags & TWL4030_WRST_SCRIPT) {
 483                err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT,
 484                                R_SEQ_ADD_WARM);
 485                if (err)
 486                        return err;
 487        }
 488        if (flags & TWL4030_WAKEUP12_SCRIPT) {
 489                err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT,
 490                                R_SEQ_ADD_S2A12);
 491                if (err)
 492                        return err;
 493        }
 494        if (flags & TWL4030_WAKEUP3_SCRIPT) {
 495                err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT,
 496                                R_SEQ_ADD_S2A3);
 497                if (err)
 498                        return err;
 499        }
 500        if (flags & TWL4030_SLEEP_SCRIPT) {
 501                err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT,
 502                                R_SEQ_ADD_A2S);
 503                if (err)
 504                        return err;
 505        }
 506
 507        err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0,
 508                        TWL4030_PM_MASTER_PROTECT_KEY);
 509        if (err)
 510                pr_err("TWL4030 Unable to relock registers\n");
 511
 512        return err;
 513}
 514
 515/*
 516 * In master mode, start the power off sequence.
 517 * After a successful execution, TWL shuts down the power to the SoC
 518 * and all peripherals connected to it.
 519 */
 520void twl4030_power_off(void)
 521{
 522        int err;
 523
 524        err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, PWR_DEVOFF,
 525                               TWL4030_PM_MASTER_P1_SW_EVENTS);
 526        if (err)
 527                pr_err("TWL4030 Unable to power off\n");
 528}
 529
 530void __devinit twl4030_power_init(struct twl4030_power_data *twl4030_scripts)
 531{
 532        int err = 0;
 533        int i;
 534        struct twl4030_resconfig *resconfig;
 535        u8 val, address = twl4030_start_script_address;
 536
 537        err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
 538                        TWL4030_PM_MASTER_KEY_CFG1,
 539                        TWL4030_PM_MASTER_PROTECT_KEY);
 540        if (err)
 541                goto unlock;
 542
 543        err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
 544                        TWL4030_PM_MASTER_KEY_CFG2,
 545                        TWL4030_PM_MASTER_PROTECT_KEY);
 546        if (err)
 547                goto unlock;
 548
 549        for (i = 0; i < twl4030_scripts->num; i++) {
 550                err = load_twl4030_script(twl4030_scripts->scripts[i], address);
 551                if (err)
 552                        goto load;
 553                address += twl4030_scripts->scripts[i]->size;
 554        }
 555
 556        resconfig = twl4030_scripts->resource_config;
 557        if (resconfig) {
 558                while (resconfig->resource) {
 559                        err = twl4030_configure_resource(resconfig);
 560                        if (err)
 561                                goto resource;
 562                        resconfig++;
 563
 564                }
 565        }
 566
 567        /* Board has to be wired properly to use this feature */
 568        if (twl4030_scripts->use_poweroff && !pm_power_off) {
 569                /* Default for SEQ_OFFSYNC is set, lets ensure this */
 570                err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &val,
 571                                      TWL4030_PM_MASTER_CFG_P123_TRANSITION);
 572                if (err) {
 573                        pr_warning("TWL4030 Unable to read registers\n");
 574
 575                } else if (!(val & SEQ_OFFSYNC)) {
 576                        val |= SEQ_OFFSYNC;
 577                        err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, val,
 578                                        TWL4030_PM_MASTER_CFG_P123_TRANSITION);
 579                        if (err) {
 580                                pr_err("TWL4030 Unable to setup SEQ_OFFSYNC\n");
 581                                goto relock;
 582                        }
 583                }
 584
 585                pm_power_off = twl4030_power_off;
 586        }
 587
 588relock:
 589        err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0,
 590                        TWL4030_PM_MASTER_PROTECT_KEY);
 591        if (err)
 592                pr_err("TWL4030 Unable to relock registers\n");
 593        return;
 594
 595unlock:
 596        if (err)
 597                pr_err("TWL4030 Unable to unlock registers\n");
 598        return;
 599load:
 600        if (err)
 601                pr_err("TWL4030 failed to load scripts\n");
 602        return;
 603resource:
 604        if (err)
 605                pr_err("TWL4030 failed to configure resource\n");
 606        return;
 607}
 608
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.